Update State of a Component from Another in ReactJS

Viewed 185

I'm following this article (original implementation Sibling Sibling): Update state cross component

The example works perfectly. But when I try to separate each class to each .js file, then using import/export to call/bind each other. It (the updating state) doesn't work anymore. The structure like this:

Sibling1.js

import React, { Component } from 'react';
<-- some declare style -->

export function updateText(text) {
  this.setState({text})
}

export class Sibling1 extends Component {
  render() {
    return (
      <div>
        <div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
        <input style={style.textBox} type="text"
               placeholder="Write text" onChange={(e) => updateText(e.target.value)} />
      </div>
    )
  }
} 

Example.js

import React, { Component } from 'react';
import * as sibling1 from './Sibling1'; //is this good?
import {Sibling1} from './Sibling1';    //is this good?

<-- some declare style, variable -->

class Sibling2 extends Component {
  constructor(props) {
    super(props)
    this.state = {
      text: "Initial State"
    }
    sibling1.updateText = sibling1.updateText.bind(this)  //is this good binding?
  }
  render() {
    console.log('Sibling2.state : ', this.state);
    return (
      <div>
        <div style={{ ...style.topLabel, color: primaryColor }}>I am Sibling 2</div>
        <div style={style.label}>{this.state.text}</div>
      </div>
    )
  }
}

class Example3 extends Component {
  render() {
    return (
      <div>
        <Sibling1 />
        <Sibling2 />
      </div>
    )
  }
}

export default Example3;

I am simply expecting Sibling1 can change the state of Sibling2 (like the original implementation), but cannot. I guess that my bind(this) doesn't bind the right context. Can somebody tell me what are differences between the original implementation (article above) and my approach (separate to multi .js files)?

2 Answers

updateText() should be bound to a component. I'm not sure what you're trying to achieve here but updateText() might not work in Sibling1 if context changes.

You could try binding updateText() in both components (already bound in Sibling2).

import React, { Component } from 'react';

export function updateText(text) {
  this.setState({text})
}

export class Sibling1 extends Component {
  constructor() {
    updateText = updateText.bind(this)
  }
  render() {
    return (
      <div>
        <div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
        <input style={style.textBox} type="text"
               placeholder="Write text" onChange={(e) => updateText(e.target.value)} />
      </div>
    )
  }
}

Usually state is controlled in parent component if two child components need to share the state and only the handler is passed down to the chilren.

React kinda forces you to use a one way data flow. So you can't just update the state of Sibling1 from within Sibling2.

As Dinesh Pandiyan mentions in his example that you normally would have a parent component that controls the state of both the siblings. Your code would then look like this:

Sibling1.js

import React, { Component } from 'react';
<-- some declare style -->

export class Sibling1 extends Component {
  function updateText(text) {
    // Use updateText function from props.
    // Props are like state but not controlled by the component itself
    // The value is passed to the component from outside
    this.props.updateText(text)
  }

  render() {
    return (
      <div>
        <div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
        <input style={style.textBox} type="text"
               placeholder="Write text" 
               onChange={(e) => this.updateText(e.target.value).bind(this)} />
      </div>
    )
  }
} 

Example.js

import React, { Component } from 'react';
import { Sibling1 } from './Sibling1';    // This is good.
import Sibling1 from './Sibling1'; // This is also possible if you use export default class instead of export class

<-- some declare style, variable -->

class Sibling2 extends Component {
  // Use same function as in Sibling1.
  function updateText(text) {
      this.props.updateText(text)
  }

  render() {
    return (
      <div>
        <div style={{ ...style.topLabel, color: primaryColor }}>I am Sibling 2</div>
        <div style={style.label}>{this.props.text}</div> // changed state to props
      </div>
    )
  }
}

class Example3 extends Component {
  constructor(props) {
    super(props);


    this.state = {
      text: "Initial state"
    };
  }

  // Control state from parent component
  function updateText(
    this.setState({ text: text });
  }

  render() {
    return (
      <div>
        <Sibling1 updateText={this.updateText.bind(this)}/>
        <Sibling2 updateText={this.updateText.bind(this)} text={this.state.text} />
      </div>
    )
  }
}

export default Example3;
Related