Skip to content
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

Override internal table state (beta 26) #1704

Closed
afrz opened this issue Dec 4, 2019 · 12 comments
Closed

Override internal table state (beta 26) #1704

afrz opened this issue Dec 4, 2019 · 12 comments

Comments

@afrz
Copy link

afrz commented Dec 4, 2019

Describe the bug

Table Options API state.selectedRowPaths: Set<RowPathKey> does not work.

My goal is to provide selection through parent component (making react-table controlled)
but internal state override is not taken into account.

To Reproduce

  1. Go to CodeSandBox
  2. Initial state is OK (5th row is selected)
  3. State override is NOK (3th row not selected)

Expected behavior

When provided, Table Options API state should override internal state.

Desktop (please complete the following information):

  • OS : Windows
  • Browser : Chrome
  • Version : 78

Additional context

BETA react-table 26

@tannerlinsley
Copy link
Collaborator

tannerlinsley commented Dec 4, 2019 via email

@agrinko
Copy link

agrinko commented Dec 4, 2019

I am facing a somewhat related issue.
In my implementation, I had a "toggle all" button which would collapse/expand all table groups simultaneously. In beta.1 version I implemented it with the help of useTableState hook:

      // setTableState is the second element returned by useTableState hook
      setTableState(actualState => ({
        ...actualState,`
        expanded: isAlreadyExpanded ? [] : getAllExpandedState(rows) // this function collects paths of all rows that should be expanded
      }), ReactTable.actions["toggleExpanded"]);

Now that table state hook is deprecated and there is no toggleExpanded action, I'm trying to implement it with a dispatcher, like this:

      dispatch({
        type: ReactTable.actions.toggleExpandedByPath,
        path: [],
        expanded: isAlreadyExpanded ? [] : getAllExpandedState(rows)
      });

But it doesn't work, probably because toggleExpandedByPath action now requires path parameter. What should I pass to the path parameter if I want to toggle rows in bulk, not one-by-one?
I would appreciate if you help me here, or point me to the right place in the documentation, @tannerlinsley

@tannerlinsley
Copy link
Collaborator

Hmm, now that you're bringing up this use case, I am starting to wonder if a state override api is worth going back to. The reducer pattern clears up a lot of stuff with the state internally, but what you're talking about is "controlling" the final state (and it would need to be able to be memoized). I'll noodle on this a bit and get back to you.

@tannerlinsley
Copy link
Collaborator

tannerlinsley commented Dec 4, 2019

How does this look? (This is a snippet from the docs I'm writing):

How can I manually control the table state?

Occasionally, you may need to override some of the table state from a parent component or from somewhere above the usage of useTable. In this case, you can turn to useTable's useControlledState option. This hook function is run on every render and allows you an opportunity to override or change the final table state in a safe way.

For example, to control a table's pageIndex from a parent component:

const [controlledPageIndex, setControlledPage] = React.useState(0)

useTable({
  useControlledState: state => {
    return React.useMemo(
      () => ({
        ...state,
        pageIndex: controlledPageIndex,
      }),
      [state, controlledPageIndex]
    )
  },
})

It's important that the state override is done within a useMemo call to prevent the state variable from changing on every render.

@kalak451
Copy link

kalak451 commented Dec 4, 2019

This looks like a similar problem i'm having trying to drive state from react-router. Getting table state into history works cleanly, but pushing it back in when the user his the back button has been problematic. I currently push the 'state' option in with a memoized value, this works for pageIndex, but does not seem to work with sortBy.

The above solution appears workable and very close to what I am already doing.

@tannerlinsley
Copy link
Collaborator

Awesome I think it’s worth shipping and trying out then.

@tannerlinsley
Copy link
Collaborator

This is now available in beta.27

@agrinko
Copy link

agrinko commented Dec 5, 2019

Thank you @tannerlinsley , it is a solution.

But it means that now I have to take full control over the expanded state of the rows, doesn't it? Previously I could overwrite the array of expanded rows only when needed, i.e. when a user clicked "expand all / collapse all" button. Now I also need to update controlled expanded state when the user clicks on any individual expandable row.

So generic API is available, and that's the most important. But for this particular use-case, which might be quite common (just like select all / unselect all functionality), wouldn't it be nice to have a special action? Or to update semantics of ReactTable.actions.toggleExpandedByPath action to allow for a bulk update? All in all, in beta.1 I could do this using ReactTable.actions.toggleExpanded action

@afrz
Copy link
Author

afrz commented Dec 5, 2019

Thanks !
It was this kind of API I was looking for to tune the state.
I tested it successfully for my use case.

A remark : useMemo cannot be used (as described above) in useControlledState because it will "block/memoize" the internal state as long as hooks dependencies does not change.

So if controlledPageIndex does not change, internal state will be kept as is forever...

Something like that will work :

  useTable({
    useControlledState: state => {
      if (state.pageIndex !== controlledPageIndex) {
        return { ...state, pageIndex: controlledPageIndex };
      }
      return state;
    },
  })

@tannerlinsley
Copy link
Collaborator

Ah yes, you are correct. But you can simply add state to the memoization dependencies.

@serge20
Copy link

serge20 commented Jan 15, 2020

@afrz have you figured out how to control selection through a parent component? It would be great if there was an example for this.

@afrz
Copy link
Author

afrz commented Jan 15, 2020

@serge20 have you tried example given in FAQ ?

Below a constricted sample working in latest version RC15.

//in parent component
 const controlledSelectedRowIds = {
    "id-A": true,
    "id-B": true
  }

//in table definition
useTable({
   useControlledState: state => {
        if (Object.keys(controlledSelectedRowIds).length > 0)
          return { ...state, selectedRowIds: controlledSelectedRowIds };
        return state;
      },
})

In addition, you should use your own select handlers (in your custom table) to maintain selected rows state in your parent component (to update controlledSelectedRowIds in above example) and not use handlers (toggleRowSelected...) provided by the useRowSelect hook.

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

No branches or pull requests

5 participants