Skip to content

Warning: Cannot update a component from inside the function body of a different component. #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
digilist opened this issue Mar 5, 2020 · 18 comments

Comments

@digilist
Copy link

digilist commented Mar 5, 2020

Describe the bug
The warning Warning: Cannot update a component from inside the function body of a different component. appears with the latest React version 16.13 when using the example provided in the readme file.

To Reproduce
Steps to reproduce the behavior: Create a new component using the example code with React v16.13.0

Expected behavior
No warning :)

Information
This happens because the setList method is passed to the child component which results now in a warning.

More information in the react release notes: https://reactjs.org/blog/2020/02/26/react-v16.13.0.html

Versions
react-sortable = ^2.0.11
react = ^16.13.0

@swba
Copy link

swba commented Mar 13, 2020

This is because ReactSortable is compiled into a function that explicitly calls

props.setList(newList, _this.sortable, store);

If this was source code, wrapping this call with useEffect would fix the issue:

useEffect(() => { props.setList(newList, _this.sortable, store); }, []);

Bad news is that according to facebook/react#18178 (comment) this error may break the whole ReactSortable component in the nearest future...

@waynevanson
Copy link
Collaborator

After some digging, this comment describes the issue well: facebook/react#18178 (comment)

This happens because the setList method is passed to the child component which results now in a warning.

@digilist I don't see anywhere in the code where setList is passed to a child component. Would you be able to elaborate on this?

It looks like the error may be coming from this LOC, within the constructor:

props.setList(newList, this.sortable, store);

I hypothesize that we'll need to remove this from the constructor (because this may be during rendering), wait for the component to render, then call it somewhere else (componentDidUpdate, but only after first render).

If this was source code, wrapping this call with useEffect would fix the issue:

This component does not use hooks thus far. I'm actually trying to port this to hooks because it'll be easier to deal fix issues with.

@digilist
Copy link
Author

digilist commented Apr 1, 2020

@waynevanson sorry, I think I didn't express it well. I think it's coming from the callback that is passed via the setList parameter as it is used in the example here: https://github.com/SortableJS/react-sortablejs#function-component

@stevecastaneda
Copy link

Also running into this warning on ^16.13.0.

@hgeldenhuys
Copy link

hgeldenhuys commented Apr 7, 2020

I also got this is, but it was the list property, not setList. If you use useState or useSelector as your list you get this warning. If I do a "cheap" clone with JSON.parse(JSON.stringify(listFromStateOrSelector))
the warning disappears.

@stevecastaneda
Copy link

@hgeldenhuys I was able to confirm that fixed it here, but oddly enough, when I reverted back to using the state directly, the error was gone. 🤔

@waynevanson
Copy link
Collaborator

I'm not quire understanding how to replicate the issue. Would someone be able to put this into a codesandbox? I want to make sure I'm not just changing code for the hell of it.

@digilist
Copy link
Author

digilist commented Apr 8, 2020

Probably the warning is gone with the latest react versions. In my application I do not see the warning anymore 🤔

@stevecastaneda
Copy link

@digilist I agree, now on 16.13.1 and not seeing the error.

https://github.com/facebook/react/releases/tag/v16.13.1

Likely due to this?

Revert warning for cross-component updates that happen inside class render lifecycles (componentWillReceiveProps, shouldComponentUpdate, and so on). (@gaearon in #18330)

@gaearon
Copy link

gaearon commented Apr 9, 2020

Yeah.

It still pointed out a legit problem but we’ve silenced it for classes because it’s “too late” to fix there. People tend to treat constructors as places to do side effects in some legacy code.

@waynevanson
Copy link
Collaborator

I'll still address the issue. It's becoming obvious that setlist shouldn't be called within the constructor

@webdesign2be
Copy link

@waynevanson if I can help you to refactor to hooks and functional components, just let me know. :)

https://codesandbox.io/s/affectionate-blackburn-7et9h?file=/src/App.js

@eppisapia-legacy
Copy link

I also got this is, but it was the list property, not setList. If you use useState or useSelector as your list you get this warning. If I do a "cheap" clone with JSON.parse(JSON.stringify(listFromStateOrSelector))
the warning disappears.

I tried working with a copy of the list as you say but it still doesn't update the component inner state after change index

@eppisapia-legacy
Copy link

I'm not quire understanding how to replicate the issue. Would someone be able to put this into a codesandbox? I want to make sure I'm not just changing code for the hell of it.

There is a sandbox with issue
https://codesandbox.io/s/intelligent-paper-iodho?file=/src/App.js

@siwonia
Copy link

siwonia commented Aug 18, 2020

Maybe not the best solution, but adding a setTimeout solves it for me for now.

<ReactSortable
  list={items}
  setList={(nextItems) => setTimeout(() => setItems(nextItems))}
>
  {items.map((item) => (
    <Item key={item.id} {...item} />
  ))}
</ReactSortable>

https://codesandbox.io/s/sweet-varahamihira-zdb19

@waynevanson
Copy link
Collaborator

will be fixed in #175 by adding hooks

@nakedgremlin
Copy link

@waynevanson Do you know when that update #175 will be set to release? I'm encountering this exact same issue and we are working through a final QA pass before we noticed this issue surface.

I would love to stay with this library since it's been the most straightforward to integrate into our builds.

@velosipedist
Copy link

velosipedist commented Nov 7, 2020

Maybe this would help someone. I just left setList empty and used onEnd handler instead, and called a state update from it. Inside the reducer, I handle the elements s

 <ReactSortable
      list={optionsVal}
      onEnd={(evt, sortable, store) => {
        if (evt.oldIndex === undefined || evt.newIndex === undefined) {
          return;
         }
        dispatch({ type: "OPTION_MOVED", from: evt.oldIndex, to: evt.newIndex });
      }}
      setList={() => {
         // just because it is required
       }}              
     >...list...</ReactSortable>

Catching that in the reducer, I handle swapping manually:

moveItem(options, fromIndex, toIndex);

export function moveItem<T>(items: T[], from: number, to: number): T[] {
  const moveResult = [...items];
  const moved = items[from];
  moveResult.splice(from, 1);
  moveResult.splice(to, 0, moved);
  return moveResult;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests