React-table: Reducer in useRowSelect relies on external state

Created on 15 Apr 2020  路  5Comments  路  Source: tannerlinsley/react-table

Pulled out of #2120, because this should be fixed even if 2120 is solved in another way or ends up being unrelated.

Describe the bug

The handler of toggleRowSelected action type reads isSelected from row objects:

As a result, when multiple selection changes happen within the same reducer run, toggleRowSelected uses the initial row selection state, regardless of whether another action updated it earlier in the same run.

Provide an example via Codesandbox!

Sorry I did not make one. The idea is just to do this:

assert(someRow.isSelected); // the bug shows only if row is selected

toggleAllRowsSelected(false) ;
someRow.toggleRowSelected(true);

What happens then is this:

  • The reducer handles toggleAllRowsSelected.

    • It clears selectedRowIds.

    • At this point, the isSelected property of rows has not been updated.

  • The reducer handles toggleRowSelected.
  • At last, react recomputes selectedFlatRows, which updates isSelected as a side effect (see #2170).

It is hard to make a minimal example, because this triggers observable bugs depending on when exactly React chooses to recompute selectedFlatRows, which is further obfuscated by #2170.

Expected behavior

Reducers should never access any information outside of previousState. Otherwise they no longer are predictable.

Replacing row.isSelected by id in state.selectedRowIds or something similar should work I believe.

help wanted wontfix

Most helpful comment

@fparga no fix yet. I haven't seen an overwhelming amount of noise on this issue, so it doesn't seem to affect many right now. I currently have my sights set on v8 where this class of issue won't exist at all (by nature of some new architecture decisions). I would be happy to accept a PR to fix this issue in v7 and even offer any guidance on it.

All 5 comments

Yeah, I think you're right. This particular situation is further mitigated in the next branch, too where we have a new state management paradigm that gets around the stale state issue with reducers. There's a lot of places in React Table right now that could suffer from this same problem, so it seemed fitting that we move to something a bit safer.

What's the status on this issue? Is there a workaround?

@fparga no fix yet. I haven't seen an overwhelming amount of noise on this issue, so it doesn't seem to affect many right now. I currently have my sights set on v8 where this class of issue won't exist at all (by nature of some new architecture decisions). I would be happy to accept a PR to fix this issue in v7 and even offer any guidance on it.

@fparga a workaround is to

  • avoid useRowSelect, or
  • use it in controlled state mode (so, avoiding selection state update logic), or
  • use selectedRowIds directly, ignoring helper methods and isSelected attribute.
    It is an annoyance, but at least you can use all other parts of react-table normally.

Looking forward to v8 though :)

+1 to add some noise. I spent a couple hours integrating/debugging inconsistency bugs before finding this issue and tossing out the code to roll our own implementation. The design/spec looks great so I'm hopeful it's more stable in v8 馃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

krishna-shenll picture krishna-shenll  路  3Comments

bdkersey picture bdkersey  路  3Comments

tremby picture tremby  路  3Comments

LeonHex picture LeonHex  路  3Comments

kieronsutton00 picture kieronsutton00  路  3Comments