Unable to use useDispatch() inside a component

Viewed 202

I am trying to use redux to store user's login information. When he logs in, the application has to store his name and login date/time. I will present the code then I will describe the problem.

App.tsx

import { Redirect, Route } from 'react-router-dom';
import { IonApp, IonRouterOutlet, IonSpinner } from '@ionic/react';
import { IonReactRouter } from '@ionic/react-router';
import Home from './pages/Home';
import Login from './pages/Login';
import Register from './pages/Register';

import { useEffect, useState } from 'react';
import { getCurrentUser } from './pages/FirebaseServices'
import Dashboard from './pages/Dashboard';
import { useDispatch } from 'react-redux';
import { setUserDate, setUserState } from './reducers/actions';

const RoutingSystem: React.FC = () => {
  return (
    <IonReactRouter>
      <IonRouterOutlet>
        <Route exact path="/home">
          <Home />
        </Route>
        <Route exact path="/login" component={Login} />
        <Route exact path="/register" component={Register} />
        <Route exact path="/dashboard" component={Dashboard} />
        <Route exact path="/">
          <Redirect to="/home" />
        </Route>
      </IonRouterOutlet>
    </IonReactRouter>
  )
}

const App: React.FC = () => {

  const [busy, setBusy] = useState<boolean>(true)
  const dispatch = useDispatch()

  useEffect(() => {

    getCurrentUser().then((user: any) => {
      if (user) {
         //dispatch(setUserState(user.email))
         //dispatch(setUserDate(Date().toLocaleString()))
        window.history.replaceState({}, '', '/dashboard')
      } else {
        window.history.replaceState({}, '', '/')
      }
      setBusy(false)
    })
  }, [dispatch])

  return <IonApp> {busy ? <IonSpinner /> : <RoutingSystem />} </IonApp>
}

export default App;

FirebaseServices.tsx

import { resolve } from 'node:url'
import { auth } from '..'
import { toast } from './Toast'

export async function loginUser(username: string, password: string) {
  
    const email = `${username}@app.com`

    try {
        
        const res = await auth.signInWithEmailAndPassword(email, password)
        return {type: true, email: res.user?.email}

    } catch (error) {
        console.log(error.message)
        toast(error.message, 10000)
        return {type: false, email: ''}
    }
}

Login.tsx

import { IonBackButton, IonButton, IonButtons, IonContent, IonHeader, IonInput, IonLoading, IonPage, IonTitle, IonToolbar } from "@ionic/react"
import React, { useState } from "react"
import { useDispatch } from "react-redux"
import { Link } from "react-router-dom"
import { setUserDate, setUserState } from "../reducers/actions"
import { loginUser } from "./FirebaseServices"
import { toast } from "./Toast"

const Login: React.FC = () => {

    const [busy, setBusy] = useState<boolean>(false)

    const [username, setUsername] = useState('')
    const [password, setPassword] = useState('')

    const dispatch = useDispatch()

    async function login() {
       
        setBusy(true)    
        console.log(username, password)        
        
        const res : any = await loginUser(username,password)            
        
        if(res.type){  
            dispatch(setUserState(res.email))  
            dispatch(setUserDate(Date().toLocaleString()))                                      
            toast("Você logou com sucesso")
        }
        
        setBusy(false)    
    }

    
    return (
        <IonPage>
            <IonHeader>
                <IonToolbar>
                    <IonButtons slot="start">
                        <IonBackButton defaultHref="/home" />
                    </IonButtons>
                    <IonTitle slot="end">
                        Login
                    </IonTitle>
                </IonToolbar>
            </IonHeader>

            <IonLoading message="Aguarde..." isOpen={busy} duration={0} />

            <IonContent className="ion-padding">
                <IonInput placeholder="Digite o nome do usuário" onIonChange={e => setUsername(e.detail.value!)} />
                <IonInput type="password" placeholder="Digite a senha" onIonChange={e => setPassword(e.detail.value!)} />

                <IonButton color="primary" onClick={login}> Login</IonButton>

                <p>
                    Novo por aqui? <Link to="./register"> Registrar</Link>
                </p>

            </IonContent>

        </IonPage>
    )
}

export default Login

Dashboard.tsx

import { IonBackButton, IonButton, IonButtons, IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from "@ionic/react"
import React from "react"
import { signOff } from "./FirebaseServices"
import { auth } from '..'
import { useSelector } from "react-redux"

const Dashboard: React.FC = () => {        
    
    const username = useSelector((state: any) => state.user.username)    
    const date = useSelector((state: any) => state.time.lastLogin) 
    
    async function off() {
       signOff()       
    }
    
    return (              

        <IonPage>
            <IonHeader>
                <IonToolbar>
                    <IonButtons slot="start">
                        <IonBackButton defaultHref="/home" />
                    </IonButtons>
                    <IonTitle slot="end">
                        Dashboard
                    </IonTitle>
                </IonToolbar>
            </IonHeader>

            

            <IonContent className="ion-padding">
                
                <p> Nome do usuário no Redux Store {username}</p>
                <br></br>
                <p>Data: {date} </p>
                <IonButton color="primary" onClick={off}> Sign out</IonButton>
            </IonContent>

        </IonPage>
    )
}

export default Dashboard

index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';

import firebase from 'firebase/app'
import 'firebase/auth'
import { Provider } from 'react-redux';
import  reducer from "./reducers/reducers"
import { createStore } from 'redux';

firebase.initializeApp({
  apiKey: "",
  authDomain: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: ""
});

export const auth = firebase.auth();

const store = createStore(reducer)

ReactDOM.render(
  <Provider store={store}>
  <React.StrictMode>    
    <App />
  </React.StrictMode>
  </Provider>,
  document.getElementById('root')
);

serviceWorkerRegistration.unregister();

reportWebVitals();

actions.tsx

export const setUserState = (payload: any) => {
    return { type: 'SET_USER_STATE', payload}
}

export const setUserDate = (payload: string) => {
    return {type: 'SET_USER_DATE', payload}
}

reducers.tsx

const defaultState = {
    user: {},
    time: {}
     
}

export default function reducer(state = defaultState,
    { type, payload }: { type: string; payload: any }): any {

    switch (type) {
        case 'SET_USER_STATE':

            return {
                ...state,
                user: {
                    username: payload.split('@')[0],                    
                }                
            }
        case 'SET_USER_DATE':

            return {
                ...state,
                time: {                    
                    lastLogin: payload
                }
            }
        default:
            return state
    }
}

Problem

I have success using useDispatch() inside the useEffect(). However, when I try to use it inside the Login component (Login.tsx), it didn't work. What is going on?

I am reading about redux, but I can't figure out what I am missing here.

0 Answers
Related