React (MERN):组件在数据库中更新,但不在状态中

问题描述 投票:0回答:1

尝试更新组件时,它在数据库中成功更新,但状态未更新。

Home.js

import { useEffect } from "react"
import { useWorkoutsContext } from '../hooks/useWorkoutsContext'

// components
import WorkoutDetails from '../components/WorkoutDetails'
import WorkoutForm from "../components/WorkoutForm"

const Home = () => {
  const { workouts, dispatch } = useWorkoutsContext()

  useEffect(() => {
    const fetchWorkouts = async () => {
      try {
        const response = await fetch('/api/workouts')
        if (!response.ok) {
          throw new Error('Network response was not okay')
        }
        const json = await response.json()
        dispatch({ type: 'SET_WORKOUTS', payload: json })
      } catch (error) {
        console.log('Error fetching workouts: ', error)
      }
    }
    fetchWorkouts()
  }, [dispatch])

  const handleUpdateSubmit = (updatedWorkoutData) => {
    console.log(updatedWorkoutData)
    // Update the local state with the updated workout
    const updatedWorkouts = workouts.map((workout) =>
      workout._id === updatedWorkoutData._id ? updatedWorkoutData : workout,
    );
    dispatch({ type: 'SET_WORKOUTS', payload: updatedWorkouts });
  }

  return (
    <div className="home">
      <div className="workouts">
        {workouts &&
          workouts.map((workout) => (
            <WorkoutDetails
              key={workout._id}
              workout={workout}
              onUpdate={handleUpdateSubmit} // Pass the update handler to WorkoutDetails
            />
          ))}
      </div>
      <WorkoutForm />
    </div>
  )
}

export default Home

锻炼详情.js

import { useWorkoutsContext } from '../hooks/useWorkoutsContext'
import React, { useState } from 'react';

// date fns
import formatDistanceToNow from 'date-fns/formatDistanceToNow'

const WorkoutDetails = ({ workout }) => {
  const { dispatch } = useWorkoutsContext()
  const [isUpdating, setIsUpdating] = useState(false);
  const [updatedWorkout, setUpdatedWorkout] = useState({
    title: workout.title,
    load: workout.load,
    reps: workout.reps
  })

  const handleDeleteClick = async () => {
    const response = await fetch('/api/workouts/' + workout._id, {
      method: 'DELETE'
    })
    const json = await response.json()

    if (response.ok) {
      dispatch({type: 'DELETE_WORKOUT', payload: json})
    }
  }

  const handleUpdateClick = () => {
    setIsUpdating(true);
  };

  const handleInputChange = (e) => {
    const { name, value } = e.target
    setUpdatedWorkout({ ...updatedWorkout, [name]: value })
  }

  const handleUpdateSubmit = async (e) => {
    e.preventDefault();
  
    try {
      const response = await fetch('/api/workouts/' + workout._id, {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(updatedWorkout),
      });
  
      if (!response.ok) {
        throw new Error('Network response was not okay');
      }
  
      const json = await response.json();
      setIsUpdating(false);
      
      dispatch({ type: 'UPDATE_WORKOUT', payload: json });
    } catch (error) {
      console.log('Error updating workout: ', error);
    }
  };
  

  return (
    <div className="workout-details">
      <h4>{workout.title}</h4>
      <p><strong>Load: </strong>{workout.load}</p>
      <p><strong>Reps: </strong>{workout.reps}</p>
      {isUpdating ? (
        <form onSubmit={handleUpdateSubmit}>
        <label htmlFor="title">Title:</label>
        <input
          type="text"
          id="title"
          name="title"
          value={updatedWorkout.title}
          onChange={handleInputChange}
        />
        <label htmlFor="load">Load:</label>
        <input
          type="text"
          id="load"
          name="load"
          value={updatedWorkout.load}
          onChange={handleInputChange}
        />
        <label htmlFor="reps">Reps:</label>
        <input
          type="text"
          id="reps"
          name="reps"
          value={updatedWorkout.reps}
          onChange={handleInputChange}
        />
        <button type="submit">Update Workout</button>
      </form>
      ) : (
        <>
          <p>{formatDistanceToNow(new Date(workout.createdAt), { addSuffix: true })}</p>
          <span className='material-symbols-outlined' onClick={handleDeleteClick}>delete</span>
          <button className='update-btn' onClick={handleUpdateClick}>Update</button>
        </>
      )}
    </div>
  )
}

export default WorkoutDetails

WorkoutContext.js

import { createContext, useReducer } from "react";

export const WorkoutContext = createContext()

export const workoutsReducer = (state, action) => {
  switch (action.type) {
    case 'SET_WORKOUTS':
      return {
        workouts: action.payload
      }
    case 'CREATE_WORKOUT':
      return {
        workouts: [action.payload, ...state.workouts]
      }
    case 'DELETE_WORKOUT':
      return {
        workouts: state.workouts.filter((w) => w._id !== action.payload._id)
      }
    case 'UPDATE_WORKOUT':
      return {
        ...state, 
        workouts: state.workouts.map((workout) =>
          workout._id === action.payload._id ? action.payload : workout
        ),
      }
    default: 
      return state
  }
}

export const WorkoutsContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(workoutsReducer, {
    workouts: null
  })

  return (
    <WorkoutContext.Provider value={{...state, dispatch}}>
      { children }
    </WorkoutContext.Provider>
  )
}

useWorkoutsContext.js

import { WorkoutContext } from "../context/WorkoutContext";
import { useContext } from "react";

export const useWorkoutsContext = () => {
  const context = useContext(WorkoutContext)

  if (!context) {
    throw Error('useWorkoutsContext  must be inside an WorkoutsContectProvider')
  }

  return context
}

如果需要,很高兴提供更多我的代码!

谢谢!

我尝试创建一个名为“handleUpdateSubmit”的道具来在更新组件时更新组件的状态。但是,更新的唯一方法是通过手动刷新。

reactjs react-hooks state mern react-props
1个回答
0
投票

您似乎已经设置了一个上下文和减速器来管理您的锻炼状态,这是一个很好的方法。从您的代码来看,更新该状态下的锻炼的逻辑似乎是正确的。

该问题可能与您如何将 onUpdate 属性传递给 WorkoutDetails 组件有关。让我们仔细检查一些事情:

在 Home.js 中,确保正确传递 onUpdate 属性:

{workouts &&
  workouts.map((workout) => (
    <WorkoutDetails
      key={workout._id}
      workout={workout}
      onUpdate={handleUpdateSubmit} // Pass the update handler to WorkoutDetails
    />
  ))}

在 WorkoutDetails.js 中,使用 onUpdate 属性来触发更新:

const WorkoutDetails = ({ workout, onUpdate }) => {
  // ...
  
  const handleUpdateSubmit = async (e) => {
    e.preventDefault();
  
    try {
      // ... (your update logic)
  
      // Call the onUpdate function passed from Home.js
      onUpdate(updatedWorkoutData);
      
      setIsUpdating(false);
    } catch (error) {
      console.log('Error updating workout: ', error);
    }
  };
  
  // ...
}

确保更新成功后正确调用onUpdate。如果一切设置正确,更新状态应该可以工作。

© www.soinside.com 2019 - 2024. All rights reserved.