Search code examples
reactjsapollo

React doesn't update the view after login


Scenario:

i have this app that everytime the user login or logout by a mutation operation in the front i have to refresh the browser by myself to update the navbar, maybe i missing refetchQueries method before the history.push in the main function. then if i logout by clicking the signout button at navbar the same i have to refresh by myself the browser to the conditionals working at the navigation component.

    // React apollo
    import { graphql } from 'react-apollo';
    import * as compose from 'lodash.flowright';

    // React router
    import { withRouter } from 'react-router-dom';

    // import mutations
    import mutations from './mutations';

    // React bootstrap
    import { Container, Row, Form, Button } from 'react-bootstrap';

    // Import Style
    import './style.css';



    class LoginForm extends React.Component {
        
        state = {
            login_credentials: {}
        }

        get_data = async(e) => {
            e.preventDefault();
            const { name, value } = e.target;
            const data = { [name]: value };
            const newData = { ...this.state.login_credentials, ...data };
            this.setState({
                login_credentials: newData
            });

        }
        
        submit = async(e) => {
            e.preventDefault();
            const { signinUser } = this.props;
            const { login_credentials } = this.state;
            
            try {
                let variables = login_credentials;
                const response = await signinUser({variables});
                const get_token = response.data.signinUser.token;
        
                // setting localStorage
                localStorage.setItem('token', get_token);
                
                this.props.history.push('/');
            } catch(error) {
                console.log(error);
            }

        
        }


        render() {

            return(
                <Fragment>
                <Container>
                    <Form className="form-container">
                        <h2 className="text-center pb-4">Ingreso</h2>
                        <Form.Group controlId="formBasicEmail">
                            <Form.Control name='email' onChange={e => this.get_data(e)} type="email" placeholder="Email" />
                        </Form.Group>

                        <Form.Group controlId="formBasicPassword">
                            <Form.Control name='password' onChange={e => this.get_data(e)} type="password" placeholder="Contraseña" />
                        </Form.Group>
                        <div className="text-center">
                            <Button className="button-login" variant="primary" onClick={e => this.submit(e)} type="submit">
                                Ingresa
                            </Button>
                        </div>
                    </Form>
                </Container>
                </Fragment>
            );
        }
    }

    export default compose(
        withRouter,
        graphql(mutations.signinUser, { name: 'signinUser' }),
    )(LoginForm);

My routes are in the navbar component:

    class NavbarLayout extends React.Component {
    
    signOut = async(e) => {
        e.preventDefault();
        const { signoutUser } = this.props;
        try {
            await signoutUser();
            localStorage.removeItem('token');
        } catch(error) {
            console.log(error);
        }
    }

    render() {
        const user_token = localStorage.getItem('token') || '';
        return(
            <Fragment>
                <Navbar bg="light" expand="lg">
                    <Navbar.Brand href="#home">Sample App</Navbar.Brand>
                    <Navbar.Toggle aria-controls="basic-navbar-nav" />
                    <Navbar.Collapse id="basic-navbar-nav">
                        <Nav className="ml-auto">
                        <Nav.Link><Link to="/">Home</Link></Nav.Link>
                        {user_token ? (
                            <Fragment>
                                <Nav.Link onClick={e => this.signOut(e)}>Salir</Nav.Link>
                                <Nav.Link>Publicar</Nav.Link>
                            </Fragment>
                        ):(
                        <Nav.Link><Link to="/sign-in">Entrar</Link></Nav.Link>
                        )}
                        </Nav>

                    </Navbar.Collapse>

                </Navbar>
                <Switch>
                    <Route path="/sign-in" component={LoginPage} />
                </Switch>
            
            </Fragment>
        );
    }
}

export default compose(
    graphql(mutations.signoutUser, { name: 'signoutUser' })
)(NavbarLayout)

Solution

  • So these look like two different issues.

    In the sign in scenario, it looks like you have a nested routes. The page doesn't redirect because you don't have a route for / defined within the same sub-Router. You will need to move the sign in route up to the same level as the home route.

    In the sign out scenario, this is more of a general React issue, you don't do anything that would trigger a render. For example, if you moved the login state to the local state of your NavBar then you can force the component to re-render when the login state changes e.g.

    class NavbarLayout extends React.Component {
      constructor() {
        this.state = { loggedIn: false };
      }
    
      signOut = async(e) => {
        e.preventDefault();
        const { signoutUser } = this.props;
        try {
          await signoutUser();
          localStorage.removeItem('token');
          this.setState({ loggedIn: false });
        } catch(error) {
          console.log(error);
        }
      }
    
      ...
    }
    

    This state would of course need to be kept in sync when you sign in for this to work properly but you get the idea.