Home > Mobile >  Call method of functional component in a class
Call method of functional component in a class

Time:06-27

I'm trying to use the useNavigation function inside a class component, but I've found out that's not possible.

Is there any way to call the navigate function of the component LoginForm through the userStore class?

LoginForm component:

import { Alert } from "@mui/material";
import { observer } from "mobx-react-lite";
import react, { ChangeEvent, useEffect, useState } from "react";
import { useStore } from "../../app/stores/store";
import Button from "../../utils/Button";
import { useNavigate } from "react-router-dom";

export default observer (function LoginForm() {
    const navigate = useNavigate();

    const {userStore} = useStore();
    
    const [error, setError] = useState(''); 

    const initialValues = {
        email: '',
        password: ''
    }

    const [info, setInfo] = useState(initialValues);

    function handleSubmit() {
        userStore.login(info).catch(error => setError('Invalid Email or Password'));
    }

    function handleInputChange(event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
        const {name, value} = event.target;
        setInfo({...info, [name]: value});
    }
    return (
        <>
            <div className="container">
                <form>
                    <input type="text" onChange={handleInputChange} defaultValue={info.email} name="email" placeholder="Email"/>
                    <input type="password" onChange={handleInputChange} defaultValue={info.password} name="password" placeholder="Password"/>
                    <Button className="btn action maxwidth" onClick={handleSubmit}>Login</Button>
                    {error !== '' && <Alert severity="error">{error}</Alert>}
                </form>
            </div>
        </>
    )
})

userStore class:

import { makeAutoObservable, runInAction } from "mobx";
import agent from "../api/agent";
import { User, UserFormValues } from "../models/user";
import { store } from "./store";

export default class UserStore {
    user: User | null = null;
    
    constructor() {
        makeAutoObservable(this)
    }
    
    get isLoggedIn() {
        return !!this.user;
    }

    login = async (creds: UserFormValues) => {
        try {
            const user = await agent.Account.login(creds);
            store.commonStore.setToken(user.token);
            runInAction(() => this.user = user);
            /* this.props.navigate('/restaurant'); */
            console.log(user);
        } catch (error) {
            throw error;
        }
    }

    logout = () => {
        store.commonStore.setToken(null);
        window.localStorage.removeItem('jwt');
        this.user = null;
    }
}

CodePudding user response:

useNavigation can't be directly used inside class component. So you need to create a parent component which is functional and pass the useNavigation hook through props into the class component where you need to use useNavigation. So that you can use it inside the class component. Refer the below code,

class MyBackButton extends React.Component {
  render() {
    // Get it from props
    const { navigation } = this.props;
  }
}

// Wrap and export
export default function(props) {
  const navigation = useNavigation();

  return <MyBackButton {...props} navigation={navigation} />;
}

CodePudding user response:

You cannot call navigate from UserStore class, and you shouldn't, better to call it inside LoginForm

function handleSubmit() {
        userStore
           .login(info)
           .then(() => navigate('/restaurant'))
           .catch(error => setError('Invalid Email or Password'));
}

Another approach is to pass a callback

function handleSubmit() {
        userStore
           .login(info, () => navigate('/restaurant'))
           .catch(error => setError('Invalid Email or Password'));
}

// UserStore class

//...
login = async (creds: UserFormValues, callback) => {
    try {
        const user = await agent.Account.login(creds);
        store.commonStore.setToken(user.token);
        runInAction(() => this.user = user);
        callback && callback()
        console.log(user);
    } catch (error) {
        throw error;
    }
}
//...

CodePudding user response:

You can pass a callback. Using it in then() of a promise could be better approach. It is in the answer below.

function handleSubmit() {
        userStore.login(info, () => {navigate('/restaurant')}).catch(error => setError('Invalid Email or Password'));
    }



   login = async (creds: UserFormValues, afterLogin?:() => void) => {
        try {
            const user = await agent.Account.login(creds);
            store.commonStore.setToken(user.token);
            runInAction(() => this.user = user);
           if (afterLogin) {
             afterLogin();
           }
            console.log(user);
        } catch (error) {
            throw error;
        }
    }

CodePudding user response:

useNavigator is a hook, and you can not use any hook directly in a class component. In order to get it done you need to call useNavigator hook in a higher-order component and wrap the class component by it.

You can check out, How can I use React hooks in React classic class component?

  • Related