Tejas Jadhav
Tejas Jadhav

Reputation: 93

ReactJS: Maintain data state between parent and child

There's a parent, <MessageBox /> element, which contains a list of messages stored in its state. For each message in messages, a <Message /> element is created inside <MessageBox /> which has fields for message.subject and message.body. A user can edit the message.subject and message.body and once done, the message object is sent back to <MessageBox /> through a props.updateHandler() to maintain the message state in the parent.

In my current approach, I'm storing the message data in MessageBox's state and in the render() function, I'm creating the <Message /> elements and passing a callback to each of them to send back data changes. In the callback, the updated data from each of the <Message /> elements is updated back into MessageBox's state. The reason for this is to keep all the recent updated data in one place only. The above approach creates havoc if shouldComponentUpdate() method in <Message /> is not overloaded (infinite recursion).

Is there a better approach for this? I've to write a lot of code just to override the builtin methods to keep the entire thing stable. As I'm not planning to go for Flux/Redux, is there a React-only approach for this?

EDIT: Since there's a lot of confusion, I'm adding minimal code.

class Message extends React.Component {
    constructor(props) {
        this.state = {
            subject: this.props.subject,
            body: this.props.body,
            type: this.props.type,
            messageIndex: this.props.messageIndex
        };
    }

    componentDidUpdate() {
        this.props.updateHandler(messageIndex, {
            subject: this.state.subject,
            body: this.state.body,
            type: this.state.type
        });
    }

    render() {
        return (
            <div>
                <input
                    type="text"
                    defaultValue={this.state.subject}
                    onBlur={e => this.setState({subject: e.target.value})} />

                <input
                    type="text"
                    defaultValue={this.state.subject}
                    onBlur={e => this.setState({body: e.target.value})} />

                <select
                    type="text"
                    value={this.state.subject}
                    onChange={e => this.setState({type: e.target.value})}>
                    <option>Type 1</option>
                    <option>Type 2</option>
                </select>
            </div>
        )
    }
}

class MessageBox extends React.Component {
    constructor(props) {
        this.state = {
            messages: aListOfMessageObjects
        }
    }

    updateHandler(message) {
        // Message update happens here and returns a list updatedMessages
        this.setState({
            messages: updatedMessages
        });
    }

    render() {
        let _this = this;

        var messagesDOM = this.state.messages.map((m) => {
            return (
                <Message
                    message={m}
                    updateHandler={_this.updateHandler.bind(_this)} />
            );
        })

        return (
            <div>
                {messagesDOM}
            </div>
        );
    }
}

Upvotes: 0

Views: 645

Answers (2)

Maggie
Maggie

Reputation: 8071

  1. Don't do a callback to MessageBox from componentDidUpdate() of Message. Do a callback directly from an action in Message.
  2. You don't need state in Message component at all. Props will keep the values you are interested if you update parent's state properly.

What you need is something like:

    <input type="text"
       defaultValue={this.props.subject}
       onBlur={e => this.updateSubject(e.target.value)} />

    updateSubject: function(newSubjectValue) {
         this.props.updateHandler(messageIndex, {
            subject: newSubjectValue,
            body: this.props.body,
            type: this.props.type
    });
    }

That way the component will get re-rendered, but won't do another call to the parent's setState.

Upvotes: 1

Damien Leroux
Damien Leroux

Reputation: 11693

If that can help, read thinking-in-react. It explains how data should go only one way to avoid be lost in UI updates.

React ToDo MVC will provide you an example of React good practice on a real case

To know how to pass props from your parent to children read controlled-components. you'll have to use value and onBlur on each input. Any onBlur event will call this.props.updateHandler with e as parameter instead of e => this.setState({type: e.target.value}).

Upvotes: 1

Related Questions