Ayush Gupta
Ayush Gupta

Reputation: 9285

Change props in Higher-Order-Component in react

I am trying to understand higher-order-components(referred to as HOC below):

As such, I have created a sample HOC to do GET requests for my component:

import React from 'react';
import { Text } from 'react-native';
import axios from 'axios';

export default (Elem,  props = {}) => {
    // mock props for testing
    props = {
        apiRequests: {
            "todoList": {
                url: "https://jsonplaceholder.typicode.com/todos"
            }
        }
    }
    return class extends React.Component {
        componentWillMount() {
            let apis = Object.keys(props.apiRequests);
            for(let i = 0; i < apis.length; i++) {
                props.apiRequests[apis[i]].done = false
                axios.get(props.apiRequests[apis[i]].url).then((resp) => {
                    console.warn("done")
                    props.apiRequests[apis[i]].done = true
                    props.apiRequests[apis[i]].data = resp.data
                })
            }
        }

        render() {
            return (<Elem {...props} />)
        }
    }
}

Now, when I wrap my component with the above HOC, I get the props with done as false.

However, soon, when I get my API response, the HOC logs done in my console, but the data in my component isn't updated. What am I doing wrong?

Upvotes: 1

Views: 2017

Answers (1)

Estus Flask
Estus Flask

Reputation: 222369

Props are immutable. This

props.apiRequests[apis[i]].done = true

is a mistake and won't cause child component to be re-rendered.

A state that is received from asynchronous request should be stored in component state, setState triggers a re-render. componentWillMount was deprecated because it was misused for asynchronous routines. It should be:

return class extends React.Component {
    this.state = {};

    componentDidMount() {
        let apis = Object.keys(props.apiRequests);
        for(let i = 0; i < apis.length; i++) {
            axios.get(props.apiRequests[apis[i]].url).then((resp) => {
                this.setState({ apis[i]]: resp.data });
            })
        }
    }

    render() {
        return (<Elem data={this.state} />)
    }
}

Depending on how data is expected to be received, state updates could be performed in batch with Promise.all.

Upvotes: 1

Related Questions