21

Hi i just recently started learning ReactJS and been playing around the import and export functions, For example this is the structure of the app, 3 separate files the parent and 2 children; How do i export the state from InputArea to DisplayArea?

Parent Component

import React, { Component } from 'react';
import DisplayArea from './DisplayArea';
import InputArea from './InputArea';

class App extends Component {
  render() {
    return (
      <div id="wrapper" className="App">
        <DisplayArea />
        <InputArea />
      </div>
    );
  }
}

export default App;

Child 1 Component

import React, { Component } from 'react';
import InputArea from './InputArea';

class DisplayArea extends Component {
  constructor(props){
    super(props);
  }

    render() {
      return (
        <div className="column">
            <div className="col-body">
                <div id="preview">{ How to display contents here? }</div>
            </div>
        </div>
      );
    }
  }

export default DisplayArea;  

Child 2 Component

import React, { Component } from 'react';

class InputArea extends Component {
    constructor(props){
      super(props);
      this.state = {
        content: ''
      }
      this.handleChange = this.handleChange.bind(this);
    }

    handleChange(e){
      e.preventDefault();
      this.setState({
        content: e.target.value
      })
    }

    render() {
      return (
        <div className="column">

            <div className="col-body">
                <textarea id="editor" placeholder="Enter text here" onChange={this.handleChange}></textarea>
            </div>
        </div>
      );
    }
  }

export default InputArea; 
Twirlman
  • 1,109
  • 2
  • 12
  • 30
  • 3
    This is a fundemental concept with react. Passing props is a one way street unless you use some sort of state management like Redux. I already anwsered this here ==> https://stackoverflow.com/questions/47858242/dealing-with-nested-react-components-state-changes/47858356#47858356 You should try to avoid using props from one children to another lateral like you are doing, Either next the third child in the second, or use state management. Regardless of your choice you will need to create a 'updateParent' function that you can find in my previous answer. – Gavin Thomas Jul 20 '18 at 00:09

3 Answers3

12

You need to lift your state up in your situation. The second option is what @Gavin Thomas suggested in the comment. But without Redux you can do it like:

const InputArea = (props) => {
  const handleChange = (e) => props.handleInputValue(e.target.value);

  return (
    <div className="column">
      <div className="col-body">
        <textarea
          id="editor"
          placeholder="Enter text here"
          onChange={handleChange}
        ></textarea>
      </div>
    </div>
  );
};

const DisplayArea = (props) => (
  <div className="column">
    <div className="col-body">
      <div id="preview">{props.inputValue}</div>
    </div>
  </div>
);

class App extends React.Component {
  state = {
    inputValue: "Initial Value",
  };

  handleInputValue = (inputValue) => this.setState({ inputValue });

  render() {
    return (
      <div id="wrapper" className="App">
        <DisplayArea inputValue={this.state.inputValue} />
        <InputArea handleInputValue={this.handleInputValue} />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Here, we hold our input value state in the parent component, which is App. We pass a callback function to InputArea and change our parent component's state using this callback function. Then we pass this state to our DisplayArea component.

devserkan
  • 16,870
  • 4
  • 31
  • 47
4

Here are the relevant parts of the code. Basically passing a liftState method to the InputArea component which will actually update the state of App when called. Then pass content to DisplayArea as a prop.

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            content: ""
        };
    }
    liftState = state => {
        this.setState(state);
    }
    render() {
        return (
            <div className="App">
                <InputArea liftState={this.liftState}/>
                <DisplayArea content={this.state.content}/>
            </div>
        );
    }
}

class InputArea extends Component {
    handleChange(event) {
        this.props.liftState({content: event.target.value});
    }
}

class DisplayArea extends Component {
    render() {
        return (
            <div className="column">
                <div className="col-body">
                    <div id="preview">{this.props.content}</div>
                </div>
            </div>
        )
    }
}
wdm
  • 7,121
  • 1
  • 27
  • 29
3

The React.js documentation says (Lifting State Up):

Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor...

Example:

// Parent component which contains shared state
class Parent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      child1Value: 0,
      child2Value: 0,
    }

    this.handleChild1Click = this.handleChild1Click.bind(this);
    this.handleChild2Click = this.handleChild2Click.bind(this);
  }

  handleChild1Click(nextValue) {
    this.setState({ child1Value: nextValue });
  }

  handleChild2Click(nextValue) {
    this.setState({ child2Value: nextValue });
  }

  render() {
    return (
      <div>
        <Child
          value={this.state.child2Value}
          onClick={this.handleChild1Click}
        />
        <Child
          value={this.state.child1Value}
          onClick={this.handleChild2Click}
        />
      </div>
    )
  }
}


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

    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.props.onClick(this.props.value + 1);
  }

  render() {
    return (
      <div>
        <p>Value of my sibling: {this.props.value}</p>
        <button onClick={this.onClick}></button>
      </div>
    )
  }
}
Roman Mahotskyi
  • 4,576
  • 5
  • 35
  • 68
  • 1
    This answer is cool and correct, but I think everyone would appreciate some code! – Jimmy Adaro Apr 06 '19 at 03:39
  • 2
    @JimmyAdaro I've provided an example. I hope it becomes more clear for you. Consider to use a new approach of sharing state between components - https://reactjs.org/docs/hooks-intro.html – Roman Mahotskyi Apr 08 '19 at 09:57