如何使用React和Redux控制动态组件的状态?

gkn4icbw  于 2023-04-30  发布在  React
关注(0)|答案(1)|浏览(97)

如何控制使用Redux动态添加的组件的状态?当使用React控制状态时,我能够做到这一点,但当我引入Redux时,每个组件都具有相同的状态。
在下面的示例中,单击按钮以添加父项。然后单击按钮添加新的子项。再次单击“添加新父级”按钮时,第二个父级与第一个父级具有相同的子级数。是否可以单独管理每个父状态,知道可以有任意数量的父状态,或者我应该坚持使用React控制状态?
下一步是使用每个父节点的print child info按钮打印所有子节点中的文本,但由于子节点的状态与父节点的控制方式类似,我认为一旦我克服了第一个障碍,这将很容易。
下面是目录结构和代码:

├── src
│   ├── App.jsx
│   ├── appSlice.js
│   ├── components
│   │   ├── Child.jsx
│   │   ├── Parent.jsx
│   │   ├── childSlice.js
│   │   └── parentSlice.js
│   ├── main.jsx
│   └── store
│       └── store.js

main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from "react-redux";
import App from './App'
import { store } from "./store/store";

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
)

App.jsx

import { useDispatch, useSelector } from "react-redux"
import { addParent } from "./appSlice"
import Parent from "./components/Parent";

export default function App() {
  const dispatch = useDispatch();
  const {parents} = useSelector(state => state.app)

  return (
    <div style={{
      display: 'flex', 
      justifyContent: 'center', 
      flexDirection: 'column',
      margin: '2rem'
      }}>
      <button onClick={() => dispatch(addParent())}>Add parent</button>
      <br />
      {parents.map((item) => (
        <Parent key={item}/>
      ))}
    </div>
  )
}

appSlice.js

import { v4 as uuid} from "uuid"
import { createSlice } from "@reduxjs/toolkit";

const appSlice = createSlice({
  name: "app",
  initialState: {
    parents: []
  },
  reducers: {
    addParent: (state) => {
      state.parents = [...state.parents, uuid()];
    },
  },
});

export const { addParent } = appSlice.actions;

export default appSlice.reducer;

store/store。js

import { configureStore } from "@reduxjs/toolkit";
import appReducer from "../appSlice";
import parentReducer from "../components/parentSlice";
import childReducer from "../components/childSlice"

export const store = configureStore({
  reducer: {
    app: appReducer,
    parent: parentReducer,
    child: childReducer
  },
});

**components/Parent。联系我们

import React from 'react'
import Child from './Child'
import { useDispatch, useSelector } from "react-redux"
import { addChild } from "./parentSlice"

export default function Parent() {
  const dispatch = useDispatch()
  const {children} = useSelector(state => state.parent)

  return (
    <div style={{
      width: "100%",
      height: "100px",
      border: "1px solid black",
      marginBottom: "10px",
    }}>
      <button onClick={() => dispatch(addChild())}>Add new child</button>
      {children.map(item => (
        <Child key={item} />
      ))}
      <button>Print child info</button>
    </div>
  )
}

components/parentSlice。js

import { v4 as uuid} from "uuid"
import { createSlice } from "@reduxjs/toolkit";

const parentSlice = createSlice({
  name: "parent",
  initialState: {
    children: []
  },
  reducers: {
    addChild: (state) => {
      state.children = [...state.children, uuid()];
    },
  },
});

export const { addChild } = parentSlice.actions;

export default parentSlice.reducer;

**components/Child。联系我们

import React from 'react'
import { useDispatch } from 'react-redux'
import { updateText } from './childSlice';

export default function Child() {
  const dispatch = useDispatch();
  return (
    <div>
      <input 
        type='text' 
        onChange={(event) => dispatch(updateText(event.target.value))}
      ></input>
    </div>
  )
}

components/childSlice。js

import { createSlice } from "@reduxjs/toolkit";

const childSlice = createSlice({
  name: "child",
  initialState: {
    text: "",
  },
  reducers: {
    updateText: (state, {payload}) => {
      state.text = payload;
    },
  },
});

export const { updateText } = childSlice.actions;

export default childSlice.reducer;
laik7k3q

laik7k3q1#

在React中,UI是state和props的函数。在这种情况下,状态是您的Redux商店。基本上,代码中缺少的是子id值与 some 父id值的关联。所有的子节点都使用相同的state.child状态值,没有任何区别。
我建议进行以下状态切片更改:

更新状态

appSlice-这里没有什么变化,仍然是父“节点”id的数组。

const appSlice = createSlice({
  name: "app",
  initialState: {
    parents: []
  },
  reducers: {
    addParent: (state) => {
      state.parents = [...state.parents, uuid()];
    }
  }
});

parentSlice-在这里,您希望状态是一个对象,其中生成的父ID是键,值是子ID值的数组。

const parentSlice = createSlice({
  name: "parent",
  initialState: {
    children: {}
  },
  reducers: {
    addChild: (state, action) => {
      if (!state.children[action.payload]) {
        state.children[action.payload] = [];
      }
      state.children[action.payload].push(uuid());
    }
  }
});

childSlice-类似地,子状态将是一个对象,其中键是子id值,值是输入值。

const childSlice = createSlice({
  name: "child",
  initialState: {},
  reducers: {
    updateText: (state, action) => {
      state[action.payload.id] = action.payload.value;
    }
  }
});

从State渲染UI

App-选择state.app.parents(* 或Object.keys(state.parent)!* )并将此数组Map到Parent组件,并将id作为prop传递。

export default function App() {
  const dispatch = useDispatch();
  const { parents } = useSelector((state) => state.app);

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        flexDirection: "column",
        margin: "2rem"
      }}
    >
      <button onClick={() => dispatch(addParent())}>Add parent</button>
      <br />
      {parents.map((id) => (
        <Parent key={id} id={id} />
      ))}
    </div>
  );
}

Parent-类似地,获取传递的id属性并选择state.parent.children[id]以获得***this***parent的children id值数组。将它们Map到Children组件,并再次将子组件的id值作为prop传递。Parent将把它的id值传递给分派的addChild动作,这样reducer就知道要向哪个父对象添加子对象。

export default function Parent({ id }) {
  const dispatch = useDispatch();
  const children = useSelector((state) => state.parent.children[id]);

  return (
    <div
      style={{
        width: "100%",
        height: "100px",
        border: "1px solid black",
        marginBottom: "10px"
      }}
    >
      <button onClick={() => dispatch(addChild(id))}>Add new child</button>
      {children?.map((childId) => (
        <Child key={childId} id={childId} />
      ))}
      <button>Print child info</button>
    </div>
  );
}

Child-子组件将使用传递的id prop 通过state.child[id]选择其值,并呈现其输入。Child将其id值***和***输入值传递给分派的updateText action,这样reducer函数就知道哪个子ID正在更新。

export default function Child({ id }) {
  const dispatch = useDispatch();
  const value = useSelector((state) => state.child[id]);

  return (
    <div>
      <input
        type="text"
        onChange={(event) =>
          dispatch(updateText({ id, value: event.target.value }))
        }
        value={value || ""}
      />
    </div>
  );
}

Demo

相关问题