I can't use setState correctly to change value

2019-03-05 22:57发布

问题:

I am trying to use setState to change value in components state (personId), with a value (pid) that I get from inside of render().

There is three scenarios inside of this component. All of those are kind of same scenarios, but with small changes depending on what I receive from API-call with Axios.

I change state inside of componentDidMount, which I guess runs once when the component is opened, I've also tried changing state inside of componentDidUpdate which then causes infinite loop.

I also left out of chunk of code which doesn't really have anything do with this problem just to make it somewhat tidier.

class SearchPage extends React.Component {

    state = {
        personId: '',
        person_results: [],
    }

        componentDidMount(){
            //this.props.fetchPersonInfo(this.state.personId)
            let pid;
            if(this.props.results.length > 0){
                if (this.props.results[0].results[0].media_type === 'person'){
                    this.setState({person_results: this.props.results[0].results});
                    pid = this.state.person_results.id;
                } else if (this.props.results[0].results[1].media_type === 'person'){
                    this.setState({person_results: this.props.results[0].results})
                    pid = this.state.person_results.id;
                } else if (this.props.results[0].results[2].media_type === 'person'){
                    this.setState({person_results: this.props.results[0].results})
                    pid = this.state.person_results.id
                }
            }
            this.setState({
                personId: pid,
            })
        }


    render(){
            if (this.props.results.length > 0){
                if (this.props.results[0].results[0].media_type === 'person'){
                    console.log(this.props.results[0].results[0]);
                    console.log(this.state.person_results);
                    return (
                        <div className='.col-md-4 search-container'>
                                <div className='search-results-person'>
                                    <h5 className='search-title'>{this.state.person_results.name}</h5>
                                    <img className='search-image' src={`${img_url}${this.state.person_results.profile_path}`} alt={this.state.person_results.name} />
                                </div>
                        </div>
                    )
                } else if (this.props.results[0].results[1].media_type === 'person'){
                    console.log(this.state.person_results);
                    return (
                        <div className='.col-md-4 search-container'>
                                <div className='search-results-person'>
                                    <h5 className='search-title'>{this.state.person_results.name}</h5>
                                    <img className='search-image' src={`${img_url}${this.state.person_results.profile_path}`} alt={this.state.person_results.name} />
                                </div>
                        </div>
                    )
                } else if (this.props.results[0].results[2].media_type === 'person'){
                    console.log(this.state.person_results);
                    return (
                        <div className='.col-md-4 search-container'>
                                <div className='search-results-person'>
                                    <h5 className='search-title'>{this.state.person_results.name}</h5>
                                    <img className='search-image' src={`${img_url}${this.state.person_results.profile_path}`} alt={this.state.person_results.name} />
                                </div>
                        </div>
                    )
                }
            }
            return (
                <div className='container-fluid'>
                        <div className='row'>
                                {results}
                                {this.state.personId}
                                {this.state.person_results}
                        </div>
                </div>
            )
        }
    }

const mapStateToProps = (state) => {
    return {
        results: state.movTv.results,
        person: state.movTv.personInfo
    }
}

export default connect(mapStateToProps, { fetchPersonInfo })(SearchPage);

EDIT: Updated code for the component.

回答1:

Your variables pid and person_results are scoped to the module, ie they are defined "at the same level" as your SearchPage component. It is a good idea to only define consts at this level when using React (for example, your image url). Right now React can't listen to the changes of these variables and thus won't update the state at lines like this:

person_results = this.props.results[0].results[0];
pid = person_results.id;

If you want React to update the component upon variable change, you should instead define these variables as state properties inside the constructor:

class SearchPage extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      person_results = /*...*/,
      pid: /*...*/
    }
  }

  /*...*/

}

and then refactor your code accordingly.



回答2:

You should move all of the logic you have in the render function up into the componentDidMount, but where you are returning jsx you will setState instead, so that your state represents what you want to render. Then your render function should only have the jsx, which will render the data you have set in your state (in componentDidMount)

Side note, initially it seems weird that you would be calling setState after the component has 'mounted', but react will execute any setState calls in the componentDidMount before it actually renders

It will look something like -

class SearchPage extends React.Component {

    state = {
        personId: '',
        person_results: []
    }

        componentDidMount(){
            let pid;
            if (this.props.results.length > 0){
                if (this.props.results[0].results[0].media_type === 'person'){
                    person_results = this.props.results[0].results[0];
                    pid = person_results.id;
                } else if (this.props.results[0].results[1].media_type === 'person'){
                    person_results = this.props.results[0].results[1];
                    pid = person_results.id;
                } else if (this.props.results[0].results[2].media_type === 'person'){
                    person_results = this.props.results[0].results[2];
                    pid = person_results.id;
                }
            }
            this.setState({
                personId: pid,
                person_results
            })
        }


    render(){

            return (
                <div className='container-fluid'>
                        <div className='row'>
                                {this.state.personId}
                                {this.state.person_results}
                        </div>
                </div>
            )
        }
    }

const mapStateToProps = (state) => {
    return {
        results: state.movTv.results,
        person: state.movTv.personInfo
    }
}

export default connect(mapStateToProps, { fetchPersonInfo })(SearchPage);

it can be refactored more, but it'll give you an idea of where to start