我正在实施健身追踪器,并且在渲染子组件时遇到麻烦取决于索引。
单击按钮后,“锻炼”组件应显示位于所选索引上的对象,但该对象不会重新呈现。我尝试添加日志,并且日志中的值正在更改,但只是未重新呈现。任何建议将不胜感激。
我的主要功能是
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>
);
}
谢谢, 杰克
答案 0 :(得分:0)
我找到了解决方案。问题是因为我在子组件“锻炼”中有状态,并且由于它是从父组件“ App”更改而没有更新,所以我没有更新它。我使用了forwardRef,useRef和useImperativeHandle来更新父组件的状态。现在它正在按预期工作。有关更新的实现,请参见下文。有关这些钩子的更多信息,请查看官方的ReactJS指南,或者https://itnext.io/changing-children-state-from-another-component-with-react-hooks-5c982c042e8是我找到解决方案的地方
App.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>
);
}
Workout.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;