繁体   English   中英

ReactJS,它不会在状态改变时重新渲染子组件

[英]ReactJS, it won't rerender child component on state change

我正在实现健身追踪器,并且在呈现子组件时遇到问题取决于索引。

单击按钮后,Workout 组件应显示所选索引上的对象,但不会重新渲染。 我试图添加日志,并且在日志中的值正在改变,但它只是没有重新渲染。 任何建议将不胜感激。

我的主要功能是

import React, { useState } from "react";
import Workout from "./Workout";
import sampleData from "./sampleData";

export default function App() {
  const [exerciseIndex, setExerciseIndex] = useState(0);
  const [renderWorkout, setRenderWorkout] = useState([
    sampleData[exerciseIndex]
  ]);

  const handleClick = currentIndex => {
    //console.log(currentIndex);
    setExerciseIndex(currentIndex);
    setRenderWorkout(sampleData[exerciseIndex]);
  };

  const navVal = sampleData.map((exercise, index) => {
    return (
      <button key={index} onClick={() => handleClick(index)}>
        {exercise.date}
      </button>
    );
  });

  return (
    <div className="App">
      <h1>Workouts</h1>
      <br />
      {navVal}
      <Workout initialValues={renderWorkout[exerciseIndex]} />
    </div>
  );
}

和 sampleData 如下所示

const data = [
  {
    workout: [
      {
        exerciseId: 1,
        exerciseName: "BenchPress",
        sets: [
          {
            setId: 1,
            weight: 75,
            reps: 3
          }
        ]
      },
      {
        exerciseId: 2,
        exerciseName: "Deadlift",
        sets: [
          {
            setId: 2,
            weight: 135,
            reps: 2
          }
        ]
      },
      {
        exerciseId: 3,
        exerciseName: "Squat",
        sets: [
          {
            setId: 3,
            weight: 135,
            reps: 2
          }
        ]
      }
    ],
    date: 1
  },

  {
    workout: [
      {
        exerciseId: 1,
        exerciseName: "Squat",
        sets: [
          {
            setId: 1,
            weight: 75,
            reps: 3
          }
        ]
      },
      {
        exerciseId: 2,
        exerciseName: "Good Morning",
        sets: [
          {
            setId: 2,
            weight: 135,
            reps: 2
          }
        ]
      },
      {
        exerciseId: 3,
        exerciseName: "Leg Raise",
        sets: [
          {
            setId: 3,
            weight: 135,
            reps: 2
          }
        ]
      }
    ],
    date: 22
  }
];

export default data;

锻炼组件

import React, { useReducer } from "react";
import "./styles.css";
import update from "react-addons-update";

/*const initialValues = {
  ...sampleData,
  newExerciseName: ""
};*/

export default function Workout(props) {
  let newData;
  function reducer(state, action) {
    switch (action.type) {
      case "topTextChange":
        return {
          ...state,
          [action.payload.name]: action.payload.value
        };

      case "addSet":
        newData = update(state.workout, {
          [action.payload - 1]: {
            sets: {
              $push: [
                {
                  setId: state.workout[action.payload - 1].sets.length + 1,
                  weight: 0,
                  reps: 0
                }
              ]
            }
          }
        });
        return {
          ...state,
          workout: newData
        };

      case "removeSet":
        newData = update(state.workout, {
          [action.payload - 1]: {
            sets: {
              $splice: [[state.workout[action.payload - 1].sets.length - 1, 1]]
            }
          }
        });
        return {
          ...state,
          workout: newData
        };

      case "addExercise":
        console.log(state);
        newData = update(state.workout, {
          $push: [
            {
              exerciseId: state.workout.length + 1,
              exerciseName: state.newExerciseName,
              sets: []
            }
          ]
        });
        return {
          ...state,
          workout: newData
        };
      case "deleteExercise":
        const position = state.workout.findIndex(
          exercise => exercise.exerciseId === action.payload
        );
        newData = update(state.workout, {
          $splice: [[position, 1]]
        });
        return {
          ...state,
          workout: newData
        };
      default:
        throw new Error();
    }
  }

  const [state, dispatch] = useReducer(reducer, props.data);

  const addExercise = () => dispatch({ type: "addExercise" });
  const deleteExercise = id =>
    dispatch({ type: "deleteExercise", payload: id });

  const changeText = e =>
    dispatch({ type: "topTextChange", payload: e.target });

  const renderedExercise = state.workout.map((exercise, index) => {
    console.log(exercise);

    return <Exercise key={index} exercise={exercise} />;
  });

  function Set(props) {
    // ToDo: hook up below values to states
    return (
      <div>
        <label>
          {" "}
          Weight <input type="text" value={props.weight} />{" "}
        </label>
        <label>
          {" "}
          Reps <input type="text" value={props.rep} />{" "}
        </label>
      </div>
    );
  }

  function Exercise(props) {
    const renderedSet = props.exercise.sets.map((set, index) => (
      <Set key={index} weight={set.weight} rep={set.reps} />
    ));

    const addSet = id => dispatch({ type: "addSet", payload: id });
    const deleteSet = id => dispatch({ type: "removeSet", payload: id });

    return (
      <div>
        {props.exercise.exerciseName}
        <button onClick={() => deleteExercise(props.exercise.exerciseId)}>
          {" "}
          Remove Exercise{" "}
        </button>
        <button onClick={() => addSet(props.exercise.exerciseId)}>
          {" "}
          Add Set{" "}
        </button>
        <button onClick={() => deleteSet(props.exercise.exerciseId)}>
          Remove Set
        </button>
        {renderedSet}
      </div>
    );
  }

  return (
    <div className="Workout">
      {renderedExercise}
      <br />
      <br />
      <input
        type="text"
        name="newExerciseName"
        value={state.newExerciseName}
        placeholder="What exercise do you want to add?"
        onChange={e => changeText(e)}
      />{" "}
      <button onClick={() => addExercise()}>Add Exercise</button>
    </div>
  );
}

谢谢,杰克

我找到了解决办法。 问题是因为我在子组件“锻炼”中有状态,并且我没有更新它,因为它是从父组件“应用程序”更改的。 我使用 forwardRef、useRef 和 useImperativeHandle 来更新父组件的状态。 现在它按预期工作。 有关更新的实施,请参见下文。 有关这些钩子的更多信息,请查看官方 ReactJS 指南或https://itnext.io/changed-children-state-from-another-component-with-react-hooks-5c982c042e8是我找到解决方案的地方

应用程序.js

import React, { useState, useRef } from "react";
import Workout from "./Workout";
import sampleData from "./sampleData";

export default function App() {
  const workoutRef = useRef(null);

  const navVal = sampleData.map((exercise, index) => {
    return (
      <button key={index} onClick={() => handleClick(index)}>
        {exercise.date}
      </button>
    );
  });

  const handleClick = currentIndex => {
    workoutRef.current.updateExercise(sampleData[currentIndex]);
  };

  return (
    <div className="App">
      <h1>Workouts</h1>
      <br />
      {navVal}
      <Workout ref={workoutRef} data={sampleData[0]} />
    </div>
  );
}

锻炼.js

import React, { useReducer, forwardRef, useImperativeHandle } from "react";
import "./styles.css";
import update from "react-addons-update";

const Workout = forwardRef((props, ref) => {
  let newData;
  function reducer(state, action) {
    switch (action.type) {
      case "topTextChange":
        return {
          ...state,
          [action.payload.name]: action.payload.value
        };

      case "addSet":
        newData = update(state.workout, {
          [action.payload - 1]: {
            sets: {
              $push: [
                {
                  setId: state.workout[action.payload - 1].sets.length + 1,
                  weight: 0,
                  reps: 0
                }
              ]
            }
          }
        });
        return {
          ...state,
          workout: newData
        };

      case "removeSet":
        newData = update(state.workout, {
          [action.payload - 1]: {
            sets: {
              $splice: [[state.workout[action.payload - 1].sets.length - 1, 1]]
            }
          }
        });
        return {
          ...state,
          workout: newData
        };

      case "addExercise":
        console.log(state);
        newData = update(state.workout, {
          $push: [
            {
              exerciseId: state.workout.length + 1,
              exerciseName: state.newExerciseName,
              sets: []
            }
          ]
        });
        return {
          ...state,
          workout: newData
        };
      case "deleteExercise":
        const position = state.workout.findIndex(
          exercise => exercise.exerciseId === action.payload
        );
        newData = update(state.workout, {
          $splice: [[position, 1]]
        });
        return {
          ...state,
          workout: newData
        };
      case "replaceExercise":
        return {
          ...action.payload
        };
      default:
        throw new Error();
    }
  }

  const [state, dispatch] = useReducer(reducer, props.data);

  const addExercise = () => dispatch({ type: "addExercise" });
  const deleteExercise = id =>
    dispatch({ type: "deleteExercise", payload: id });

  const changeText = e =>
    dispatch({ type: "topTextChange", payload: e.target });

  const renderedExercise = state.workout.map((exercise, index) => {
    return <Exercise key={index} exercise={exercise} />;
  });

  const updateExercise = data => {
    dispatch({ type: "replaceExercise", payload: data });
  };

  useImperativeHandle(ref, () => {
    return {
      updateExercise: updateExercise
    };
  });

  function Set(props) {
    // ToDo: hook up below values to states
    return (
      <div>
        <label>
          {" "}
          Weight <input type="text" value={props.weight} />{" "}
        </label>
        <label>
          {" "}
          Reps <input type="text" value={props.rep} />{" "}
        </label>
      </div>
    );
  }

  function Exercise(props) {
    const renderedSet = props.exercise.sets.map((set, index) => (
      <Set key={index} weight={set.weight} rep={set.reps} />
    ));

    const addSet = id => dispatch({ type: "addSet", payload: id });
    const deleteSet = id => dispatch({ type: "removeSet", payload: id });

    return (
      <div>
        {props.exercise.exerciseName}
        <button onClick={() => deleteExercise(props.exercise.exerciseId)}>
          {" "}
          Remove Exercise{" "}
        </button>
        <button onClick={() => addSet(props.exercise.exerciseId)}>
          {" "}
          Add Set{" "}
        </button>
        <button onClick={() => deleteSet(props.exercise.exerciseId)}>
          Remove Set
        </button>
        {renderedSet}
      </div>
    );
  }

  return (
    <div className="Workout">
      {renderedExercise}
      <br />
      <br />
      <input
        type="text"
        name="newExerciseName"
        value={state.newExerciseName}
        placeholder="What exercise do you want to add?"
        onChange={e => changeText(e)}
      />{" "}
      <button onClick={() => addExercise()}>Add Exercise</button>
    </div>
  );
});

export default Workout;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM