Skip to content
Advertisement

Call function in parent component without using props

I have a an App component that renders a CustomerForm component like so:

App.js

class App extends Component{
    constructor(props){
        this.state = {logged_in: true};

    }

    logout = () => {
      this.setState({logged_in: false}):
    }
    render(){
        return(
            <div>
                <CustomerForm />
            </div>
        )
    }
}

In my CustomerForm component, I have an onSubmit function that is called when the form submits:

CustomerForm.js

class CustomerForm extends Component{
    
    constructor(props){
         this.state = { submitting: false }
    }

    handleSubmit = (e) => {
       e.preventDefault();
       this.setState({submitting: true});
       try{
           //API CALL HERE
       }catch(e){
           //if error == unauthorized, then logout. 
       }

    }
}

When I render the CustomerForm component from the App component, I realize that I could pass a reference to logout via a prop, however, my application is much larger than the one I have provided here. Ultimately, I want to be able to call the logout() function that is in the App component from either like a helper file or any nested component (preferably a helper file). Once this function is called, the state for the App component would be updated to reflect that the user is now logged out. I tried to use createContext and this allowed me to pass the attributes and functions I needed, however the functions were only available in the JSX like so:

<authContext.Consumer>
    {({logout}) => {
      return (
    
        <Form onSubmit={logout}/>
      );
    }}
</authContext.Consumer> 

However, I need to be able to access logout from within the handleSubmit function. I know that I can pass the logout function as a reference to handleSubmit, but I figure there is a cleaner way to do this.

Advertisement

Answer

With Context api and HOC, you can do that. let me know your thought.

app.js

/ Create a new context for the app
export const AppContext = React.createContext('app');

class App extends Component{
    constructor(props){
        this.state = {logged_in: true};
    }

    logout = () => {
      this.setState({logged_in: false}):
    }
    render(){
        return(
            <AppContext.Provider
                value={{
                    state: this.state,
                    logout: this.logout
                }}
            >
                <CustomerForm />
            </AppContext.Provider>
        )
    }
}

Create a HOC for your Context

withAppContext.js

import {AppContext} from './app.js'

export function withAppContext(Component) {
    return function WrapperComponent(props) {
        return (
            <AppContext.Consumer>
                {state => <Component {...props} context={state} />}
            </AppContext.Consumer>
        );
    };
}

CustomerForm.js

import { withAppContext } from 'withAppContext.js'

class CustomerForm extends Component{
    
    constructor(props){
         this.state = { submitting: false }
    }

    handleSubmit = (e) => {
       this.props.context.logout();
    }

  render() {
    return (
      <>
        <div>Child</div>
        <button onClick={this.handleSubmit}>logout</button>
      </>
    );
  }

}

export default withAppContext(CustomerForm); 

codesandbox demo

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement