Skip to content

Update parent component state with a function whose arguments rely on child component

I know it seems like a duplicate from That question but it’s not, I need to do something slightly different that’s not working.

I’m implementing:

  • a componente <Page/> which has two child components:
    • a <Greetings/> message.
    • a <LoginButton/> button.

<Page/> has a {isLogged: false} state. <Greetings/> should display different messages when isLogged is true/false.

<LoginButton/> should have a login text when isLogged‘s false, logout otherwise, and implement a function that updates isLogged state from its parent component, <Page/>.

With this implementation, I’m willing to learn:

  • <Greetings/>: How to implement a child component whose props are binded to the parents component state (works)
  • <LoginButton/>: How to update parent’s state from a child component, passing parent’s setState for the child through its props (is not working).

Here’s my source code:

import React from 'react';
import ReactDOM from 'react-dom';


class Greetings extends React.Component{
  render(){
    if(this.props.isLogged){
      return <p>You're logged. Welcome Back!</p>
    }
    return <p>You're not logged. Please Login</p>
  }
}

class LoginButton extends React.Component{
  login(){
    console.log(this.props)
    this.props.handler({isLogged: true})
  }

  logout(){
    this.props.handler({isLogged: false})
  }
  
  render(){
    if(this.props.isLogged){
      return <button onClick = {()=>{this.logout()}}>logout</button> 
    }
    return <button onClick = {()=>{this.login()}}>login</button>
  }
}

class Page extends React.Component{
  constructor(props){
    super(props)
    this.state = {isLogged: false}
  }

  handler(newState){
    this.setState(newState)
    console.log(this.state)
  }

  render(){
    return(
      <>
      <Greetings isLogged={this.state.isLogged}/>
      <LoginButton isLogged={this.state.isLogged} handler = {()=>this.handler}/>
      </>
    )
  }
}

const element = <Page/>
ReactDOM.render(element, document.getElementById('root'))

The main difference between my code and most of examples from the other StackOverflow question is that it’s up to the child component, <LoginButton/>, to decide the arguments for calling this.render, which’s causing me problems.

I believe there’s something wrong on login() and logout() functions, but i’m not managing to discover what.

enter image description here

The page renders, but the button’s not working. Please helpe me.

Answer

This is because you are expecting and argument in your this.handle but not passing the value from the props. this should fix your problem.

class Page extends React.Component{
  constructor(props){
    super(props)
    this.state = {isLogged: false}
  }

  handler(newState){
    console.log(newState)
    this.setState(newState)
  }

  render(){
    return(
      <>
      <Greetings isLogged={this.state.isLogged}/>
      <LoginButton handler = {(newState) => this.handler(newState)}/>
      </>
    )
  }
}

Or you can aslo do it by binding your handler function and simply passing it to the child component as prop, rather than calling it.

class Pageextends React.Component{
  constructor(props){
    super(props)
    this.state = {isLogged: false}
    this.handler = this.handler.bind(this);
  }

  handler(newState){
    console.log(newState)
    this.setState(newState)
  }

  render(){
    return(
      <>
      <Greetings isLogged={this.state.isLogged}/>
      <LoginButton handler = {this.handler}/>
      </>
    )
  }
}