组件更改子组件所有实例的状态?"/>
如何从父组件更改子组件所有实例的状态?
我目前有一个网格(父组件),它在 2D 数组中保存一堆节点(子组件)。每个节点都有自己的“fill_color”状态。单击或拖动鼠标时可以用灰色填充节点。我在网格组件中有一个按钮,单击该按钮后,应将所有节点重置回白色。由于我设计组件的方式,特别是状态,我很难找到实现此目的的方法。我想我需要一种方法来传递一个 prop,告诉每个节点是否按下了清除按钮,但这会导致问题,因为 React 如何执行渲染/重新渲染。我做了一些研究,听说了“将状态提升到共同祖先”的概念,但我认为这仅在处理兄弟组件时相关,而不是父/子组件。
这是我的第一个 React 项目(完全不熟悉标准的 React 设计实践),所以我有一种感觉,在使用 React 时,我的 OOP 背景可能对我的伤害大于帮助。我觉得这让这个简单的功能变得比应有的困难得多。我也不确定维护二维数组中的所有节点是否是解决此问题的正确方法,这可能是我遇到麻烦的原因。我这样做只是因为它当时对我来说很直观,而且我读到的一个快速的 stackoverflow 答案说这不是问题,尽管我认为这很大程度上取决于用例。
有些人还告诉我,对于这样的简单项目,我不需要使用
useReducer
。我同意,因为我使用它只是因为我注意到 React 文档中与 useState
一起提到了它,并决定一时兴起使用它。然而,我不认为这是我的问题的根源 - 尽管我可能是错的。
如果有人可以仔细阅读代码并指出如何根据我当前的代码实现此功能,我将不胜感激。无论如何,我计划进行一次完整的重构/重新设计,但如果知道是否有一种相对简单的方法可以用当前状态的代码来完成我想要的事情,那还是很高兴的。
// Grid.js
import React, { useReducer, Button } from 'react';
import { Node } from './Node';
function reducer(state, action) {
switch (action.type) {
case "resize_rows": {
return {
...state,
num_rows: action.num_rows,
};
}
case "resize_cols": {
return {
...state,
num_cols: action.num_cols,
};
}
case "set_mouse_up_down": {
return {
...state,
is_mouse_down: !state.is_mouse_down
};
}
default:
return state;
}
}
const initialState = {
num_rows: 30,
num_cols: 50,
is_mouse_down: false,
};
export function Grid() {
const [state, dispatch] = useReducer(reducer, initialState);
const grid = [];
for (let i = 0; i < state.num_rows; i++) {
const row = []
for (let j = 0; j < state.num_cols; j++) {
if (i === state.num_rows / 2 && j === 0)
row.push(<Node key={`${i}-${j}`} startNode={true} endNode={false} is_mouse_down={state.is_mouse_down}/>);
else if (i === state.num_rows / 2 && j === state.num_cols - 1)
row.push(<Node key={`${i}-${j}`} startNode={false} endNode={true} is_mouse_down={state.is_mouse_down}/>);
else
row.push(<Node key={`${i}-${j}`} startNode={false} endNode={false} is_mouse_down={state.is_mouse_down}/>);
}
grid.push(row);
}
function handleOnMouseDown() {
dispatch({ type: "set_mouse_up_down" });
}
function handleOnMouseUp() {
dispatch({ type: "set_mouse_up_down" });
}
function clearGrid(){
// todo
}
return (
<div
className="grid-container"
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
userSelect: "none"
}}
onMouseDown={handleOnMouseDown}
onMouseUp={handleOnMouseUp}
>
<div
className="grid"
style={{
border: "1px solid black",
display: "grid",
gridTemplateColumns: `repeat(${state.num_cols}, 1fr)`,
gridTemplateRows: `repeat(${state.num_rows}, 1fr)`,
width: `75vw`,
height: `75vh`,
}}
>
{grid}
</div>
<button onClick={clearGrid}>Clear Grid</button>
</div>
);
}
// Node.js
import React, { useReducer } from 'react';
const initialState = {
fill_colour: "white",
visited: 0,
is_start: false,
is_end: false,
};
function reducer(state, action) {
switch (action.type) {
case "set_start": {
return {
...state,
is_start: true,
fill_colour: "green",
};
}
case "set_end": {
return {
...state,
is_end: true,
fill_colour: "red",
};
}
case "set_wall": {
return {
...state,
fill_colour: state.fill_colour === "white" ? "grey" : "white",
};
}
case "reset_node": {
return {
...state,
fill_colour: "white"
};
}
default:
return state;
}
}
export function Node({ startNode, endNode, is_mouse_down }) {
const [state, dispatch] = useReducer(reducer, initialState);
if (startNode && !state.is_start) {
dispatch({ type: "set_start" });
} else if (endNode && !state.is_end) {
dispatch({ type: "set_end" });
}
function handleOnMouseEnter() {
if (is_mouse_down && (!state.is_start && !state.is_end)) {
dispatch({ type: "set_wall" });
}
}
function handleOnMouseDown(){
if(!state.is_start && !state.is_end){
dispatch({ type: "set_wall" });
}
}
function resetNode(){
dispatch({type: "reset_node"});
}
return (
<div
className="node"
style={{
border: "1px solid black",
width: "100%",
height: "100%",
boxSizing: "border-box",
position: "relative",
backgroundColor: `${state.fill_colour}`,
}}
onMouseEnter={handleOnMouseEnter}
onMouseDown={handleOnMouseDown}
></div>
);
}
提前致谢。
回答如下:更多推荐
如何从父组件更改子组件所有实例的状态?
发布评论