Lucas Beier
Lucas Beier

Reputation: 631

React Router, Redux and async API call

When the user clicks in a link, React Router will display a component. The data this component will show came from an endpoint, so I'm wondering what's the best practice.

I've created a function called fetchData which uses fetch to perform a GET in an endpoint and then returns a promise. Once this promise is resolved, I would like to dispatch a Redux action to update the state.

I managed to do this with redux-thunk, but I would like to implement this without adding more libraries.

I'm trying to follow the 'container/presentational' idea and I'm using stateless functional components in React.

In a general overview, this is what I'm doing:

index.js

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

app.js

const App = () => (
    <div>
      <BrowserRouter>
        <div>
          <Header />
          <Switch>
            <Route exact path="/" component={Main} />
            <Route exact path="/data-list" component={DataListContainer} />
          </Switch>
        </div>
      </BrowserRouter>
    </div>
)

dataListContainer.js

const mapStateToProps = (state) => {
  return { data: state.data }
}

const mapDispatchToProps = (dispatch) => {
  return { }
}

const DataListContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(DataList)

dataList.js

const DataList = (props) => {
  const rows = props.data.map(data => {
    return <Data data={data} />
  })
  return (
    <div>
      {rows}
    </div>
  )
}

And the Data component simply displays the data. I'm wondering where I should add the call to the function fetchData and where I should solve the returned promise. I imagine I will need to dispatch an action after the promise is resolved, but not sure where is the best place to do this.

Other question is: I would like to fetch the data only once, I mean, only when the user clicks the /data-list link. If it comes back to main page and then goes again to data-list, I would like to not call the endpoint again. Is there any call once feature hidden in React Route implementation?

Upvotes: 0

Views: 2187

Answers (2)

TheNickyYo
TheNickyYo

Reputation: 2389

Change dataListContainer.js to be a stateful React component, this is ok because it's the container! Don't take the code for exact, it's to just to give an idea.

import { fetchData, DataList, store } '......'

class DataListContainer extends React.Component {
  componentDidMount() {
    if (!this.props.data) {
      store.dispatch(fetchData())
    }
  }
  render() {
    return (<DataList data={this.props.data}/>);
  }
}

const mapStateToProps = (state) => {
  return { data: state.data }
}

export default connect(
  mapStateToProps
)(DataListContainer)

Upvotes: 2

n9iels
n9iels

Reputation: 895

I don't think this is possible in the routes and views. When you switch pages with React Router, the component unmounts. All the data of the component is also cleared. (Believe me, this is something you want. Otherwise you might get some serieus memory issues that let your browser crash). Also, your view is only responsible for displaying stuff and should not do things with the data it receives.

Take a look for some implementation in your store. For example store the received data from the API in the store object. The next time someone is calling the fetchData function in the store, serve the stored data instead of make a new request. The store never unmounts so it can hold data in memory. Keep in mind that the user will only receive new data if the reload the entire page. So a 'hard refresh' but might be useful..

Another thing you can do is asking yourself why you don't want to call that endpoint multiple times? Is the data set to large to receive? In that case use pagination and serve it in pieces.

Upvotes: -1

Related Questions