简体   繁体   中英

In React, how can a parent component set state from the response an async fetch function performed in a child component?

In my react application, I have a child component LoginForm as follows:

export class LoginForm extends React.Component {
    constructor(props) {
        super(props);
        this.login = this.login.bind(this);
    }

    async login() {
        let data = new FormData();
        data.append('User[email]', document.getElementById('app-login-form-email').value);
        data.append('User[password]', document.getElementById('app-login-form-password').value);
        let loginHandler = this.props.loginHandler;
        const req = await fetch('/site/login', {
            method: 'POST',
            body: data
        });
        const respJson = await req.json();
        loginHandler(respJson);
    }

    render() {
        return(
            <div id="app-login-form">
                <input type="text" id="app-login-form-email" value="test@app.com"></input>
                <br/>
                <input type="password" id="app-login-form-password" value="admin"></input>
                <br/>
                <button onClick={this.login}>Login</button>
            </div>
        );
    }
}

It has a prop loginHandler , which is a function passed down from the parent component, seen here in full:

export class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            user: {
                isAuthenticated: props.isAuthenticated || false
            }
        }
        this.bodyHandlers = {
            handleUserLogin: this.handleUserLogin,
        };
        this.handleUserLogin = this.handleUserLogin.bind(this);
    }

    bodyHandlers;
    headerHandlers;
    footerHandlers;

    handleUserLogin(responseJson) {
        this.setState({
            user: {
                isAuthenticated: true,
                data: responseJson.user,
                jwt: responseJson.token
            }
        });
    }

    render() {
        return(
            <Router>
                <Header user={this.state.user} handlers={this.headerHandlers}/>
                <Body user={this.state.user} handlers={this.bodyHandlers}/>
                <Footer user={this.state.user} handlers={this.footerHandlers}/>
            </Router>
        );
    }
}

const domContainer = document.querySelector('#main');
ReactDOM.render(React.createElement(App), domContainer);

However whenever I try this, I get the following error in the console: Uncaught (in promise) TypeError: Cannot read property 'setState' of undefined

I have a basic understanding that when working within the bounds of a promise, this becomes tricky to deal with. I have tried many different permutations, including setting a variable local to the function that to this . I have also tried the fetch .then syntax too, which makes no difference.

Am I going to have to implement the fetch code as a function in the parent component and pass it down as a prop for the child component to call, or am I missing something?

Thanks.

I think the problem is that you are assigning the method as a value to bodyHandlers object before binding it. Try switching the order:

constructor(props) {
    super(props);
    this.state = {
        user: {
            isAuthenticated: props.isAuthenticated || false
        }
    }

    this.handleUserLogin = this.handleUserLogin.bind(this);
    this.bodyHandlers = {
        handleUserLogin: this.handleUserLogin,
    };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM