简体   繁体   中英

Why does this component not update when you use setState in a constructor in an asynchronous call - ReactJS/ReactNative?

** EDITED / RE-PHRASED **

After A few good answers to my question, I have edited the code in the question to state what I do not understand as clearly as I can.

I have a withErrorHandler that will be used as an hoc for Many Individual components.

One of those components is MyComponent.

The code inside componentWillMount of withErrorHandler Works as expected, but since it is a depricated method, I have to move it somewhere.

Location - ComponentDidMount interceptor will not be able to intercept the error.

Location - Constructor interceptor will intercept the error but the Modal inside the withError handler will not be shown to the user.

What exactly is happening here?

import React from 'react';
import Modal from '../../components/UI/Modal/Modal';
import Aux from '../Aux/Aux';

const withErrorHandler = (WrappedComponent, axiosInstance) => {
    return class extends React.Component {
        constructor(props) {
            super(props);
            this.state= {
                error: null
            };
        }
        
        componentWillMount() {
            let { request, response} = axiosInstance.interceptors;
            this.requestInterceptor = request.use(req => {
                this.setState({error: null});
                return req;
            })
            this.responseInterceptor = response.use(resp => resp, error => {
                console.log("axiosInstance Intercepted Error\t",error)
                this.setState({error})
            })
        }

        componentWillUnmount() {
            let { request, response} = axiosInstance.interceptors;
            request.eject(this.requestInterceptor);
            response.eject(this.responseInterceptor);
        }

        errorConfirmedHandler = () => {
            this.setState({error: null})
        }

        render() {
            return <Aux>
                <Modal show={this.state.error} modalClosed={this.errorConfirmedHandler}>
                    {this.state.error ? this.state.error.message : null}
                </Modal>
                <WrappedComponent {...this.props} />
            </Aux>
        }
    }
}

export default withErrorHandler;
import React, { Component } from 'react';
import axiosInstance from '../../AxiosInstance';
import Spinner from '../../components/UI/Spinner/Spinner';
import withErrorHandler from '../../hoc/withErrorHandler/withErrorhandler';

class MyComponent extends Component {

    constructor(props) {
        super(props);
        this.state = {
            ingredients: null,
            error: false
        }
    }

    componentDidMount() {
        // Deliberately placed Error in 'ingredients.json' by removing n
        axiosInstance.get('ingredients.jso').then(response => {
            this.setState({
                ingredients: response.data
            })
        }).catch(error => {
            console.log(`Unable to load Ingredients\nError:\t${JSON.stringify(error, null, 2)}`)
            this.setState({ error: true})
        })
    }

    render() {
        return (
                this.state.ingredients ?
                    <p>show ingredients</p> :
                    this.state.error ? <p>Ingredients Can't be loaded</p> : <Spinner />
        );
    }
}

export default withErrorHandler(MyComponent, axiosInstance);

You should not execute any side effects in the constructor , it should basically be used for initializing the local state (and other variables) or binding functions with the content.

Secondly, in react you should use the following methods carefully as they are called both on server and client.

  1. constructor
  2. componentwillmount (deprecated)
  3. getderivedstatefromprops

The better way to do this would be to make the state update in the componentDidMount lifecycle method. This method will be triggered once in the whole lifecycle of the component.

class Component1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      result: null,
    };
    this.fetchResult = this.fetchResult.bind(this)
  }

  componentDidMount() {
    this.fetchResult()
  }

  fetchResult () {
    axiosInstance
      .get(url)
      .then((result) => {
        console.log("result", result);
        this.setState({ result });
      })
      .catch((error) => {});
  }

  render() {
    return (
      <>
        <h1>SomeText</h1>
        <p>{this.state.result ? "Hey, I got the result" : "Waiting..."}</p>
      </>
    );
  }
}

Put network calls such as axiosInstance.get(url) in the componentDidMount() lifecycle method, not in the constructor. Like so:

class Component1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      result: null,
    };
  }
  
  componentDidMount() {
    axiosInstance
      .get(url)
      .then((result) => {
        console.log("result", result);
        this.setState({ result });
      })
      .catch((error) => {});
  }
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM