5

I am trying to load some initial data before rendering my components in ReactJs. I am also using the Flux architecture for this process. Here is my container:

class MemoryApp extends React.Component {
    constructor(props){
        super(props);
        MemoryActions.getInit();
        this.state = {};

    }

    componentWillMount(){
        MemoryStore.on("init", () => {
            this.setState({
                memory: MemoryStore.getInit()
            });
        })
    }

    render(){
        return (
            <div>
                <button type="button">Get Random Memory</button>
                <h1>Memory App</h1>
                <MemoryImage memory={this.state.memory}/>
                <MemoryText memory={this.state.memory}/>
            </div>
        );
    }
}

So in this file, I am calling the action getInit() which calls an API to fetch some data. Once this data is received, the init event will be emitted by the store:

class MemoryStore extends EventEmitter {

    getInit(){
        return this.memory_response;
    }

    initLoad(response){
        this.memory_response = response;
        this.emit("init");
    }

    handleActions(action){

        switch (action.type) {
            case MemoryConstants.GET_INIT_RESPONSE:{
                this.initLoad(action.response);
                break;
            }
            default:{
                return true;
            }
        }
    }

}

const memoryStore = new MemoryStore();
dispatcher.register(memoryStore.handleActions.bind(memoryStore));
export default memoryStore;

Then, as you can see, we set the state afterwards in the componenWillMount(). Then I want to render the components. One of the components is the image: import React from 'react'

export default class MemoryImage extends React.Component{
    renderItems(){

        console.log(this.props); // ===> Here is my console.log
        if (this.props.memory){
            return this.props.memory[0].description;
        } else {
            return "Nothing";
        }
    }

    renderImage(){
        if (this.props.memory){
            return this.props.memory[0].image;
        } else {
            return "Nothing";
        }
    }

    render(){
        return(
            <div>
                <p>{this.renderItems()}</p>
                <img style={{width:'200px'}} src={this.renderImage()} alt="none"/>
            </div>
        );
    }
}

And after logging onto the console...

memory-image.js:10 {memory: undefined}
memory-image.js:10 {memory: Array(1)}

It appears to me that the components are rendered before I set the state in the componentWillMount() function. I do not want this, I only want the components to render after the the state has been set in the componentWillMount(). Any ideas?

anderish
  • 1,709
  • 6
  • 25
  • 58

2 Answers2

3

Yes, it is rendered, because right before the component mounts, you simply add en event listener:

MemoryStore.on("init", () => {
    this.setState({
        memory: MemoryStore.getInit()
    });
})

The function the returns (before the event), and renders.

The simplest solution (IMO) would be:

getMemoryItems() {
    if (this.state.memory) {
        return <div>
            <MemoryImage memory={this.state.memory}/>
            <MemoryText memory={this.state.memory}/>
        </div>
    }
}

render(){
    return (
        <div>
            <button type="button">Get Random Memory</button>
            <h1>Memory App</h1>
            { this.getMemoryItems() }
        </div>
    );
}

or you could do:

render(){
    return (
        <div>
            <button type="button">Get Random Memory</button>
            <h1>Memory App</h1>
            { this.state.memory ? <MemoryImage memory={this.state.memory}/> : '' }
            { this.state.memory ? <MemoryText memory={this.state.memory}/> : '' }
        </div>
    );
}
dave
  • 62,300
  • 5
  • 72
  • 93
2

The reason is that you are only registering an event handler in componentWillMount, which will be called when that event happens sometime in future (i.e. init in your case). componentWillMount will just register it and simply continue its flow (i.e. calling render function etc). Only when that event is fired, you will get memory state.

So if you don't want to render anything until memory state is available then you can simply put a condition in render like this

render(){
        if(!this.state.memory){
           // Just wait for the memory to be available
           return null;
        }
        return (
            <div>
                <button type="button">Get Random Memory</button>
                <h1>Memory App</h1>
                <MemoryImage memory={this.state.memory}/>
                <MemoryText memory={this.state.memory}/>
            </div>
        );
    }
Prakash Sharma
  • 15,542
  • 6
  • 30
  • 37