• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

React redux - login is not a function

Object Storage Arubacloud
0 głosów
263 wizyt
pytanie zadane 2 czerwca 2023 w JavaScript przez DzikieHarce Użytkownik (690 p.)

Jestem nowa w reduxie, dostaję błąd:

TypeError: login is not a function at onSubmit.

 

Login.js:

import { Box, Button, Paper, TextField, Typography } from "@mui/material";
import React, { useEffect, useRef, useState, useContext } from "react";
import AuthContext from "../context/authProvider";
import { Link, redirect } from "react-router-dom";
import { connect } from "react-redux";
import { login } from "../actions/auth";

export const Login = ({login}) => {

  const [formData, setFormData] = useState({
    email: "",
    password: "",
  });
  const { email, password } = formData;

  const onChange = (e) =>
    setFormData({ ...formData, [e.target.name]: e.target.value });

  const onSubmit = e => {
    e.preventDefault();
    login(email, password);
  };



  return (
    <>
      {success ? (
        <Typography>OK </Typography>
      ) : (
        <Box
          width="100%"
          height="90vh"
          display="flex"
          justifyContent="center"
          alignItems="center"
        >
          <Paper sx={{ height: "40vh", width: 280, padding: "1rem" }}>
            <Typography
              ref={errRef}
              className={errMsg ? "errMsg" : "offscreen"}
              aria-live="assertive"
            >
              {errMsg}
            </Typography>
            <Typography variant="h4">Login</Typography>
            <Box display="flex" flexDirection="column" padding="1rem">
              <form onSubmit={(e) => onSubmit(e)}>
                <TextField
                  size="small"
                  placeholder="Username"
                  sx={{ marginBottom: "1rem" }}
                  onChange={(e) => onChange(e)}
                  ref={userRef}
                  value={email}
                  name="email"
                  id="username"
                  required
                ></TextField>
                <TextField
                  type="password"
                  size="small"
                  name="password"
                  placeholder="Password"
                  sx={{ marginBottom: "2rem" }}
                  onChange={(e) => onChange(e)}
                  value={password}
                  id="password"
                  required
                ></TextField>
                <Button size="large" variant="contained" type="submit">
                  Login
                </Button>
              </form>
              <Typography variant="caption" sx={{ marginTop: "1.5rem" }}>
                Dont have account yet?{" "}
                <Link
                  to="/register"
                  style={{ textDecoration: "none", color: "inherit" }}
                >
                  Click here
                </Link>
              </Typography>
              <Typography variant="caption" sx={{ marginTop: "0.5rem" }}>
                Forgot password? {" "}
                <Link
                  to="/resetPassword"
                  style={{ textDecoration: "none", color: "inherit" }}
                >
                  Reset Password
                </Link>
              </Typography>
            </Box>
          </Paper>
        </Box>
      )}
    </>
  );
};

export default connect(null,{login} )(Login)

auth.js:

import { LOGIN_SUCCESS, LOGIN_FAIL, LOAD_USER_SUCCESS, LOAD_USER_FAIL} from './types';
import api from '../api/posts';


export const load_user = () => async dispatch => {
    if(localStorage.getItem('access')){
        const config = {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `JWT ${localStorage.getItem('access')}`,
                'Accept': 'application/json'
            }
        };
        try{
            const response = await api.get('auth/v1/users/me/', config);
            dispatch({
                type: LOAD_USER_SUCCESS,
                payload: response.data
            })
        }catch(err){
            dispatch({
                type: LOAD_USER_FAIL,
            })
        }
    } else{
        dispatch({
            type: LOAD_USER_FAIL,
        })
    }
}

export const login = (email, password) => async (dispatch) => {
    const config = {
        headers: {
            'Content-Type': 'application/json'
        }
    };
    const body = JSON.stringify({email, password});

    try{
        const response = await api.post('auth/v1/jwt/create/', body, config);
        dispatch({
            type: LOGIN_SUCCESS,
            payload: response.data
        });
        dispatch(load_user());
    }catch(err){
        dispatch({
            type: LOGIN_FAIL,
        })
    }
};

App.js:

import { Box } from "@mui/material";
import "./App.css";
import NavBar from "./components/navbar";
import { Route, Routes } from "react-router-dom";
import { HomePage } from "./homepage/homePage";
import { Login } from "./login/Login";
import { Register } from "./register/Register";
import { AccountSettings } from "./accountSettings/AccountSettings";
import { JobOffers } from "./jobOffers/JobOffers";
import { UserJobOffer } from "./userJobOffer/UserJobOffer";
import { AddJobOffer } from "./addJobOffer/AddJobOffer";
import { JobOfferDetails } from "./jobOfferDetails/JobOfferDetails";
import { EditOffer } from "./editOffer/EditOffer";

import { Provider } from "react-redux";
import store from "./store";
import Layout from "./hocs/Layout";

function App() {
  return (
    <>
      <Provider store={store}>
        <Layout>
          <Routes>
            <Route exact path="/" element={<HomePage />} />
            <Route exact path="/login" element={<Login />} />
            <Route path="/register" element={<Register />} />
            <Route path="/settings" element={<AccountSettings />} />
            <Route path="/allOffers" element={<JobOffers />} />
            <Route path="/userOffers" element={<UserJobOffer />} />
            <Route path="/addJobOffer" element={<AddJobOffer />} />
            <Route path="/offerDetails/:id" element={<JobOfferDetails />} />
            <Route path="/addOffer" element={<AddJobOffer />} />
            <Route path="/editOffer" element={<EditOffer />} />
          </Routes>
        </Layout>
      </Provider>
    </>
  );
}

export default App;

store.js:

import { createStore, applyMiddleware } from "redux";
import { configureStore } from '@reduxjs/toolkit'
import {composeWithDevTools} from 'redux-devtools-extension';
import thunk from 'redux-thunk'
import rootReducer from './reducers';

const initialState = {};

const middleware = [thunk];

const store = configureStore(
    {reducer: rootReducer},
    initialState,
    composeWithDevTools(applyMiddleware(...middleware))
)

export default store;

Chciałam nauczyć się reduxa z tego filmiku, sprawdzając kod, mam prawie identyczny:

https://www.youtube.com/watch?v=5gnixz0Q3co&list=PLJRGQoqpRwdfoa9591BcUS6NmMpZcvFsM&index=7&ab_channel=BryanBrkic

Czy błąd może być powodowany inną wersją reduxa? CreateStore z tego co widziałam, zmienił się na ConfigureStore.

1 odpowiedź

+1 głos
odpowiedź 3 czerwca 2023 przez ScriptyChris Mędrzec (190,190 p.)

Na oko, to komponent Login nie powinien przyjmować propsa login, bo ta nazwa przesłania importowaną funkcję login, a że w trakcie renderowania prop login nie jest przekazywany, to ma wartość undefined, która nie jest funkcją, stąd błąd.

komentarz 3 czerwca 2023 przez ScriptyChris Mędrzec (190,190 p.)

Ah, nie zauważyłem, że na dole jest wołany connect z przekazaniem tam funkcji login i komponentu Login:

export default connect(null,{login} )(Login)

Sprawdziłbym zatem czym jest zmienna login tuż po jej imporcie oraz czym jest prop login wewnątrz komponentu Login.

komentarz 3 czerwca 2023 przez DzikieHarce Użytkownik (690 p.)

Faktycznie, po imporcie wygląda tak:

za to wewnątrz komponentu, w onSubmit jest już (parameter) login: any. Rozumiem w takim razie, że to nie te same loginy i dlatego wywala błąd? Problem z nazewnictwem?

1
komentarz 3 czerwca 2023 przez ScriptyChris Mędrzec (190,190 p.)

Dodaj console.log pod importem oraz w komponencie i pokaż co one wypisują do konsoli przeglądarki:

import { login } from "../actions/auth";
console.log('imported login:', login); // <-- TUTAJ
export const Login = ({login}) => {
  console.log('login prop:', login); // <-- TUTAJ
  const onSubmit = e => {
    console.log('onSubmit login:', login); // <-- TUTAJ

    e.preventDefault();
    login(email, password);
  };

 

komentarz 4 czerwca 2023 przez DzikieHarce Użytkownik (690 p.)

Import działa, reszta to undefined (login prop i onSubmit login). Zmieniłam nazwę w imporcie i na onSubmit na login1 i zaczęło działać (znaczy, brak errora, to nie znaczy, że działa). Problem z nazewnictwem?

console log na import:

komentarz 9 czerwca 2023 przez DzikieHarce Użytkownik (690 p.)
Zrobiłam komponent Register na podobnej zasadzie, ale tutaj zamiast login przyjmował signup, więc o błędzie nazewnictwa trudno rozmawiać, i mam ten sam problem.

Podobne pytania

0 głosów
1 odpowiedź 156 wizyt
pytanie zadane 16 sierpnia 2019 w JavaScript przez Xeryas Początkujący (250 p.)
0 głosów
1 odpowiedź 185 wizyt
pytanie zadane 31 sierpnia 2019 w JavaScript przez michal_php Stary wyjadacz (13,700 p.)
0 głosów
1 odpowiedź 185 wizyt
pytanie zadane 26 lipca 2020 w JavaScript przez rob Bywalec (2,440 p.)

92,556 zapytań

141,403 odpowiedzi

319,559 komentarzy

61,942 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...