首页 > 解决方案 > 将对象链接到 redux 商店中 react-flow-renderer/react-flow 中的元素

问题描述

我一直在尝试将商店连接到我现有的工具包,useStoreState因为useSelector我不确定它是否适合我的用例。我想知道是否有办法将商店直接连接到我现有的 redux 工具包商店,或者我是否必须始终dispatch将它连接到商店。

我当前的问题是我试图将活动元素放入我的商店并让每个对象链接另一个项目,但是由于useStoreState返回整个对象,当我dispatch进入商店时,它又变得不可序列化。这意味着,每当删除/添加元素时,它都会刷新 redux 工具包存储中存储的状态,这反过来用新对象更新存储,但所有链接的对象都会失去与该元素的连接并被删除。

我的状态示例:

[{elementID: 1, linkedText: "bla bla"}, {elementID: 2, linkedText: "bla bla for 2"}, {elementID: 3}]

当我向它添加/删除一个元素时,比如说元素 3,这会将状态重置为

[{elementID: 1}, {elementID: 2}]

哪个删除所有存储的链接文本?

我可以看看这里有什么可能的工作吗?也许能够以某种方式将结果直接存储到现有的 redux 工具包存储中?我知道easy peasy被使用,我尝试研究如何将它连接到我也未能实现的商店。

export default connect((state) => ({ elements: state}))(EditorReactFlow);

我还应该在 redux devtools 中看到 react-flow-renderer 的内部存储吗?我只能看到我自己的应用程序,所以我想不是吗?

编辑:

目前我发送的是来自实际 react-flow-renderer 的元素,下面我已经发布了该组件。useEffect元素被分派到我addStep()的 redux 切片中,该切片将其解构以获取 ID 并将其存储在商店中。

function EditorReactFlow() {
  const classes = useStyles();
  const reactFlowWrapper = useRef(null);
  const [reactFlowInstance, setReactFlowInstance] = useState(null);
  const [elements, setElements] = useState(initialElements);
  const [name, setName] = useState("")
  const dispatch = useDispatch();

  useEffect(() =>{
    dispatch(addStep(elements))
  },[dispatch, elements])

  const onConnect = (params) => setElements((els) => addEdge(params, els));
  const onElementsRemove = (elementsToRemove) =>
    setElements((els) => removeElements(elementsToRemove, els));

  const onLoad = (_reactFlowInstance) =>{
    _reactFlowInstance.fitView();
    setReactFlowInstance(_reactFlowInstance);}

  const addNode = () => {
      setElements(e => e.concat({
          id: getId(),
          data: {label: `${name}`},
          position: {x: 0, y: -5}, 
      },
      ))
  };

  return (
    <Container maxWidth="lg">
      <CssBaseline />
      <div className={classes.paper}> </div>
      <div className="{classes.heroContent} reactflow-wrapper" ref={reactFlowWrapper}>
          <ReactFlow 
            elements={elements} 
            onElementsRemove={onElementsRemove}
            style={{width:'100%',height: '90vh'}}
            onLoad={onLoad}
            onConnect = {onConnect}
            >            
            <Controls /> 
          </ReactFlow>
        <input type="text"
          onChange={e => setName(e.target.value)}
          name="title"/>
          <button 
          type="button"
          onClick={addNode}
          >Add Node</button>
      </div>
      <RfEditorSidebar />
    </Container>
  );
}

export default connect(
  (state) => ({ todos: state})
)(EditorReactFlow);

我使用 WYSIWYG 编辑器将数据存储到特定元素。AddStep 创建 elementID,然后editStep将 WYSIWYG 数据存储到该特定 ID。以下是我的切片中的 theaddStepeditStepreducer:

addStep(state, action) {
  const allSteps = action.payload;
  return {
    ...state,
    data: allSteps.map((element) => {
      return { element: element.id };
    }),
  };
},
editStep(state: any, action) {
  return {
    ...state,
    data: state.data.map((step) =>
      step.element === action.payload.element
        ? { ...step, step: action.payload.step }
        : step
    ),
  };
},

标签: reactjsreact-reduxredux-toolkit

解决方案


EditorReactFlow将“元素”存储为内部状态和 redux 的全局状态,这是有问题的。

  • 元素存储为EditorReactFlow组件的内部状态:const [elements, setElements] = useState(initialElements);
  • 元素也用于dispatch更新 Redux 全局状态的调用:dispatch(addStep(elements))
  • 这是有问题的,因为这两种状态都会尝试以自己的方式更新视图/反应组件,而且通常情况下,为您的数据提供两种不同的真实来源是很糟糕的。

通常,如果一个状态需要存储在 Redux 中,那么它是通过 Redux 检索和更新的,而不是 using useState,这不是 Redux 的一部分。

useState请考虑使用connect将 Redux 状态映射到props来代替,方法是执行以下操作:

  • 消除const [elements, setElements] = useState(initialElements);

  • 将函数签名从更改function EditorReactFlow() {function EditorReactFlow(props) {

  • 而不是使用elements变量来读取元素,使用props.todos.data

  • 删除此调用useEffect(有关说明,请参见下一项)

    useEffect(() =>{
      dispatch(addStep(elements))
    },[dispatch, elements])
    
  • 不使用setElements函数更新元素列表,而是直接调用

    dispatch(addStep(...)) /* can be repeated for other actions*/

    作为任何回调函数的一部分,例如addNode.


编辑:

最后,你EditorReactFlow应该是这样的:

function EditorReactFlow(props) {
  const classes = useStyles();
  const reactFlowWrapper = useRef(null);
  const [reactFlowInstance, setReactFlowInstance] = useState(null);
  const [name, setName] = useState("")
  const dispatch = useDispatch();

  let elements = props.todos.data;  // Redux passes elements list through props

  const onConnect = (params) => { 
    // Here, addEdge should be a Redux action, and needs to be dispatched
    dispatch(addEdge(params, elements));
  };

  const onElementsRemove = (elementsToRemove) => {
    // Here, removeElementsshould be a Redux action, and needs to be dispatched
    dispatch(removeElements(elementsToRemove, elements));
  };

  const onLoad = (_reactFlowInstance) =>{
    _reactFlowInstance.fitView();
    setReactFlowInstance(_reactFlowInstance);}

  const addNode = () => {
      dispatch(addStep(elements.concat({
          id: getId(),
          data: {label: `${name}`},
          position: {x: 0, y: -5}, 
      }))
  };

  return (
    <Container maxWidth="lg">
      <CssBaseline />
      <div className={classes.paper}> </div>
      <div className="{classes.heroContent} reactflow-wrapper" ref={reactFlowWrapper}>
          <ReactFlow 
            elements={elements} 
            onElementsRemove={onElementsRemove}
            style={{width:'100%',height: '90vh'}}
            onLoad={onLoad}
            onConnect = {onConnect}
            >            
            <Controls /> 
          </ReactFlow>
        <input type="text"
          onChange={e => setName(e.target.value)}
          name="title"/>
          <button 
          type="button"
          onClick={addNode}
          >Add Node</button>
      </div>
      <RfEditorSidebar />
    </Container>
  );
}

export default connect(
  // Ask Redux to pass Redux state to "props.todos"
  // According to your "addStep" reducer, elements are stored as "state.data"
  // So, elements list will be passed as "props.todo.data"
  (state) => ({todos: state})  
)(EditorReactFlow);

这可能会为您指明正确的方向。请让我知道这可不可以帮你。


推荐阅读