react-refetch icon indicating copy to clipboard operation
react-refetch copied to clipboard

Add state-dependent mapping, not only props.

Open eyalw opened this issue 9 years ago • 3 comments

Scenario You have a Container component that has logic and data-fetching (refetch decorated component). The container parents many presentational components, like filter selection, and table view. The filters affect the data fetched from the server, and the table view displays the results.

Now since the hierarchy is this:

  • Connect (Refetch)
    • Container (state: filters)
      • Table View (presentational)
      • Filters (presentational)

The connect mapping cannot use the filtersState to change the data fetching based on filter selection.

Suggested solution What I suggest is adding a "containerState" feature (or a better name) to React-Refetch. The feature will send "setContainerState()" function prop down to Container, and instead of the container holding state, the Connect will, and it will be able to use it for the mapping.

I'll attach the code I wrote to implement this, so we can discuss it.

eyalw avatar Jul 29 '16 15:07 eyalw

Would you be able to instead put the presentation state in an anchor/hash in the URL and pass it down as props?

ryanbrainard avatar Aug 02 '16 04:08 ryanbrainard

how about just using withState from https://github.com/acdlite/recompose/blob/master/docs/API.md#withstate ? Wouldn't this do what you're after @eyalw ? This is what I do in my components when i need something similar

compose(
  withState('containerState', 'setContainerState', null),
  connect(...)
)(MyComponent)

nfcampos avatar Aug 02 '16 12:08 nfcampos

After watching this React Rally talk by @ryanflorence I've started experimenting with the child-as-a-function style for react-refetch instead of HOC. Using this pattern, this issue is not a problem.

// Create a wrapper component to facilitate the child as a function pattern
class Connector extends React.Component {
  static propTypes = {
    mappings: React.PropTypes.object.isRequired,
    children: React.PropTypes.func.isRequired,
  }

  render() {
    const { children, ...rest } = this.props
    return children(rest)
  }
}
Connector = apiConnect(({ mappings }) => ({
  ...mappings
}))(Connector)

// Component with state
class StatefulComponent extends React.Component {
  state = { userId: 1 }

  render() {
    return (
      <div>
        <UserSelector onSelect={id => this.setState({ userId: id })} />
        <Connector
          mappings={{
            user: `http://someapi.com/user/${this.state.userId}`
          }}
        >
          {({ user }) => {
            if (!user.fulfilled) return <Loading />

            return (
              <UserDetail user={user.value} />
            )
          }}
        </Connector>
      </div>
    )
  }
}

This may not be everyone's cup of tea, but it makes the state problem a non-issue. I'm liking it so far.

TimothyKrell avatar Sep 08 '16 16:09 TimothyKrell