Submit action immediately after setting a state variable

I have an initial state of reduction:

{ loggedInUserId: null, comments: [] } 

Here's what the React app looks like:

 class App extends Component { componentWillMount() { this.props.getLoggedInUserId(); } render() { return ( <Switch> <Route exact path="/" component={HomePage} /> <Route path="/comments" component={Comments} /> </Switch> ); } } 

In my application, I dispatch the getLoggedInUserId() action, which asynchronously populates loggedInUserId in state.

HomePage is a dumb component that displays some text. I launch the application (the route is now "/"), see the HomePage component, then I go to the "Comments" page, which has:

 componentWillMount() { this.props.fetchComments(this.props.loggedInUserId); // Dispatch action to do API call to fetch user comments } render() { // Show this.props.comments nicely formatted } 

Everything works, I see a list of comments in the Comments component.

But if I refresh the page on the /comments route, then by the time Comments start componentWillMount, loggedInUserId is not loaded yet, so it will call fetchComments(null) .

Right now, to fix this, I'm doing in the Comments component:

 componentWillMount() { if (!this.props.loggedInUserId) return; this.props.fetchComments(this.props.loggedInUserId); } componentWillReceiveProps(nextProps) { if (!this.props.loggedInUserId && nextProps.loggedInUserId) { nextProps.fetchComments(nextProps.loggedInUserId); } } 

which works well. But I do it in 10+ components, and it seems like a lot of work that can be factorized, but I have not found an elegant way to do this.

So, I ask you, how do you deal with this situation? Any idea is welcome:

  • SPECIAL
  • side effects
  • other libraries
+5
source share
4 answers

I use a wrapper around Route that checks if users are logged in and if not, redirect them to the login page. Wrapped routes are displayed only after the authenticated user userId is retrieved.

 import * as React from 'react' import { Route, Redirect } from 'react-router-dom' import URLSearchParams from 'url-search-params' class AuthRoute extends React.Component { componentDidMount() { if (!this.props.isLoading) { this.props.getLoggedInUserId() } } render() { if (this.props.isLoading) { // first request is fired to fetch authenticated user return null // or spinner } else if (this.props.isAuthenticated) { // user is authenticated return <Route {...this.props} /> } else { // invalid user or authentication expired // redirect to login page and remember original location const search = new URLSearchParams({ next: this.props.location.pathname, }) const next = this.props.location.pathname !== '/' ? `?${search.toString()}` : '' return <Redirect to={`/login${next}`} /> } } } 

You need to update the reducer that handles the getLoggedInUserId action to preserve the isLoading state as well.

+3
source

You probably want the initial state to be displayed by the server in index.html (or whatever you have) and hydrated on the client.

This initial state will include loggedInUserId and data for the /comments page.

Check out https://redux.js.org/docs/recipes/ServerRendering.html

+3
source

I think using HOC will be clean. Since all the common logic will be in one place. Use composition here. Say you have components A, B, C, D

Now you want to write some general function in the componentWillReceiveProps component life cycle for all components.

Write HOC as:

 class HOC extends React.Component { componentWillReceiveProps(nextProps) { //Your commomn logic } render() { const childrenWithProps = React.Children.map(this.props.children, child => React.cloneElement(child, { ...this.props, }) return ( <div> {childrenWithProps} </div> ) } } 

Write your components as follows:

 class A extends React.Component { componentWillReceiveProps(nextProps) { //your uncommone logic } render(){ return ( <HOC {...this.props}> <div> //Your page jsx </div> </HOC> ) } } 

write the same for components B, C, and D. This pattern is useful when there is much in common between components. So better take a look at your usecase

+2
source

Record OP. Having read some good ideas here, I decided to go with the usual HOC:

 import React, { Component } from 'react'; const requireProp = (As, propsSelector, propsToDispatch) => class Wrapper extends Component { componentWillMount() { if (!propsSelector(this.props) && typeof propsToDispatch === 'function') { propsToDispatch(this.props); } } render() { const { ...props } = this.props; return !!propsSelector(this.props) && <As {...props} />; } }; export default requireProp; 

To find out how I use it, see this method .

0
source

Source: https://habr.com/ru/post/1274294/


All Articles