0

I know that the question with this title has already been asked few times before but the problem is that I couldn't get an appropriate answer. So as I am new to reactJS and trying to create login logout form.

What I want to do is to pass or change a state of parent component from a child component through an event handler(When a user clicks on logout button). Below are the two Components:

First One:

class Home extends React.Component {
    constructor(props){
        super(props);
        this.state = {login : false};
    }

    login(){
       // this method updates the login.state : true
    }


    render() {
        return (
            <div>
                {this.state.login ? (<ChatBox userNick="fad" />) : (<LoginScreen onSubmit={this.login} />) }
            </div>
        );
    }
}

And Second:

class ChatBox extends React.Component{
    logout(){
        // Expecting or trying to update parent.state.login : false
        // via this method as well
    }

    render() {
        return (
            <div className="chat-box">
                <button onClick={this.logout} > Logout </button>
                <h3>Hi, {this.props.userNick} </h3>
            </div>
        );
    }
}

I have simplified these component to come on point.

What's going here? Home Component is the main parent component. Initially the state.login is false and in this situation LoginScreen Components shows up. Now, when user login through LoginScreen Component state.login updates to true, it's time to show for ChatBox Component.

Now you can see that ChatBox Component contains a button which calls a method logout to logout user. What I want is to update once again the state.login to false in Home Component When user click on the Logout Button.

I don't know how to do it, It will be appreciate if you help me.

Thanks in advance.

Syed Aqeel
  • 1,009
  • 2
  • 13
  • 36
  • add a ref to the `` component and access its state by using `this.refs[ChatBoxRef].state` – Ray Jun 15 '17 at 08:49
  • I don't understand ref please can you explain it in the answer? – Syed Aqeel Jun 15 '17 at 08:51
  • 1
    Oh no no you should not do that. What you can do however is provide a handler to `` from `` (just like what you did for ``, except to set `state.login` to `false` instead of `true`) and let `` update its state by itself. – Quentin Roy Jun 15 '17 at 08:55
  • The ref is used to return a reference to your element. You can read more here: https://facebook.github.io/react/docs/refs-and-the-dom.html – Ray Jun 15 '17 at 08:55
  • 1
    @Raymond I am sorry, but this looks like very bad idea. I am not even sure it is possible to access the state from the outside, but if that is, that is very bad practice. What you are suggesting would not even trigger re-rendering anyway because you are never supposed to update the state directly but use `setState` instead. – Quentin Roy Jun 15 '17 at 08:58
  • Possible duplicate of [How to pass data from child component to its parent in ReactJS?](https://stackoverflow.com/questions/38394015/how-to-pass-data-from-child-component-to-its-parent-in-reactjs) – Shubham Khatri Jun 15 '17 at 08:58
  • And as far as I remember ref is for DOM elements, not for React elements. – Quentin Roy Jun 15 '17 at 08:59
  • @QuentinRoy Hi, he can use that to access the child component's state and update the parent's state with `this.setState({})` – Ray Jun 15 '17 at 09:01
  • @QuentinRoy, for example: `this.setState({someCounter: this.refs[child].state.anotherCounter})` – Ray Jun 15 '17 at 09:03
  • @Raymond it will be good if you explain it to me in answer. I will be very thankful to you. – Syed Aqeel Jun 15 '17 at 09:04
  • I fail to see how that would help OP as you still need to trigger this at some point, and the trigger comes from the child, not the parent. It seems to me there is way easier way to fetch information from children (i.e. handlers which is the recommended way). I would strongly recommend against relying on such mechanisms. And anyway, again, ref gives you the root DOM element. E.g. a
    or . You won't hack your way into a component state this way.
    – Quentin Roy Jun 15 '17 at 09:08
  • @QuentinRoy, you can get the child component's state using refs. I do not know if it wasn't possible in previous versions of react, but, it is now. When you assign the ref as ` this.ref = el} />` and log it, you will get its props, context, refs, updater, and state. BUT, I do agree that it's not preferrable to use refs for everything. – Ray Jun 15 '17 at 09:24

2 Answers2

2

Do it in the same way as you are doing for Login, pass a function as a prop and call it on logout, see updates below.

const LoginScreen = () => (<div>Login Screen</div>);

class Home extends React.Component {
    constructor(props){
        super(props);
        this.state = {login : true};
        this.logout = this.logout.bind(this);
    }

    login(){
       // this method updates the login.state : true
    }

    logout() {
       // this method updates the login.state : false
       this.setState({ login: false });
    }

    render() {
        return (
            <div>
                {this.state.login ? (<ChatBox userNick="fad" onLogout={this.logout} />) : (<LoginScreen onSubmit={this.login} />) }
            </div>
        );
    }
}

class ChatBox extends React.Component{
    constructor(props) {
        super(props)
        // This makes sure `this` keeps pointing on this instance when logout is called from the outside.
        this.logout = this.logout.bind(this);
    }

    logout(){
        // Call the onLogout property.
        this.props.onLogout();
    }

    render() {
        return (
            <div className="chat-box">
                <button onClick={this.logout} > Logout </button>
                <h3>Hi, {this.props.userNick} </h3>
            </div>
        );
    }
}

ReactDOM.render(<Home />, document.querySelector('#main'));
<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="main"></div>
Quentin Roy
  • 7,677
  • 2
  • 32
  • 50
Ganhammar
  • 1,871
  • 1
  • 16
  • 29
  • 2
    This is the exact same thing you are doing for the `` component. `` provides `` with a callback using on the `onLogout` property. Upon call of this callback, `Home#logout` is executed. `` then call this callback (`this.props.onLogout()`) on reception of the click event on its ` – Quentin Roy Jun 15 '17 at 09:12
  • Please can you explain `onLogout` what is it's purpose and you are implementing this? thank you – Syed Aqeel Jun 15 '17 at 09:12
  • 2
    There is a bug, here. `this.props.logout()` should be `this.props.onLogout()`. Let me fix it. – Quentin Roy Jun 15 '17 at 09:13
  • So means of prop `onLogout` that it is a function which calling another function? – Syed Aqeel Jun 15 '17 at 09:17
  • 1
    Ok I created a running snipped and also fixed another bug about `ChatBox#logout` being unbound when passed to its button. onLogout is a property of `` that happens to be given as a function. Then yeah, that function is called by a function and will then call other functions and so on. – Quentin Roy Jun 15 '17 at 09:23
  • 2
    button.onClick > ChatBox#logout > ChatBox#props.onLogout > Home#logout > Home#setState – Quentin Roy Jun 15 '17 at 09:25
  • Yeaahhhhhh...... At last that's worked... Thank youuuuuu veryy muchhhhh.... – Syed Aqeel Jun 15 '17 at 09:30
1

You can pass an event from the Parent component to the Child component that handles the change of the state, like so:

class App extends React.Component {

  constructor() {
    super();
    this.state = { isLoggedIn: false };
  }

  _handleLogin() {
    this.setState({ isLoggedIn: true });
  }

  _handleLogout() {
    this.setState({ isLoggedIn: false });
  }

  render() {
    const { isLoggedIn } = this.state;

    return (
        <div>
       {
            isLoggedIn ?
            <ChatBox logoutEvent={this._handleLogout.bind(this)} />
          :
          <Login loginEvent={this._handleLogin.bind(this)} />
       }
        </div>
    );
  }
}

const Login = ({ loginEvent }) => (
    <button type="button" onClick={loginEvent}>Login</button>
);

const ChatBox = ({ logoutEvent }) => (
    <div>  
    <h1>This is the Chat Box!</h1>
    <button type="button" onClick={logoutEvent}>Logout</button>
  </div>
);

ReactDOM.render(
  <App />,
  document.getElementById('container')
);

Here's the fiddle

ickyrr
  • 1,963
  • 1
  • 14
  • 14