lochbrunner/react-flow-editor

Uncaught TypeError: node is undefined when using custom reducer

Pavinati opened this issue · 5 comments

Switching from demoMode true to false and using a custom update logic crashes the Editor

To Reproduce

  1. Setup a minimal working example with demoMode: true
  2. Write your custom reducer
  3. use demoMode: false and apply your reducer to the onChange callback
  4. add a new node to the editor

Expected behavior
The code executes the reducer, a new state is produced and the editor updates

Screenshots
image

Setup

  • Ubuntu 20.04
  • Firefox 80.0
  • Node 14
  • React + ES6

Additional context
Code taken from my example

const reducer = (nodes, action) => {
  switch (action.type) {
  case 'NodeCreated':
    return nodes.push(action.node);

  // other cases handling

  default:
    return nodes;
  }
};

export default () => {
  const [nodes, updateNodes] = useReducer(reducer, []);

  return (
      <div className="flow-editor-wrapper">
        <div className="flow-menu">
          { /* same as demo example */ }
        </div>
        <Editor
          nodes={nodes}
          config={{
            resolver,
            onChanged: updateNodes,
            connectionType: 'bezier',
            grid: true,
            demoMode: false,
            direction: 'we',
          }}
        />
      </div>
  );
}

Thank you for posting that issue. I try to investigate this the next days.

To be honest I am not a expert in the useReducer approach, but
I think you mixed up the onChanged callback with the reducer function.

Please have a look at the example code using redux:
https://github.com/lochbrunner/react-flow-editor/tree/master/example/redux

You have to set onChanged to a redux action not a reducer.
Be aware that 'NodeCreated' et. al. are the types of the payload of the action you registered for all editor actions:

export const reducer: Reducer<RootState> =
    (state: RootState = {nodes: [] },
     action: Action<{}|ChangeAction>) => {
     if (action.type === Actions.EDITOR_UPDATES) {
        const payload = action.payload as ChangeAction;
        if (payload.type === 'NodeCreated') {
          state.nodes.push(payload.node);
       }
     }
  }

Sadly i'm not using Redux, just ES6 + react. Didn't see in the project description that redux was required.
By the way, the function you posted behaves similarly to what mine does: takes 2 parameters, the state and the event.
Performs some action depending on the event type, and updates the state

The library does not require redux. Sorry for misunderstanding your code snippet.
But I think the outcome of useReducer might not be suitable as you are using it for the callback here.
Let me have a look tomorrow. We will definitively find a solution for your case.
Maybe we have to add another example :D

OK I found at least two issues in your code:

Do it the functional style!

the push method does not return what you expect

- return nodes.push(action.node);
+ return [...nodes, action.node];

Decouple the events!

- onChanged: updateNodes,
+ onChanged: (data) => setImmediate(() => updateNodes(data)),

I hope this works for you!
(At least this works for me)