• 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

VPS Starter Arubacloud
0 głosów
345 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ź 177 wizyt
pytanie zadane 16 sierpnia 2019 w JavaScript przez Xeryas Początkujący (250 p.)
0 głosów
1 odpowiedź 197 wizyt
pytanie zadane 31 sierpnia 2019 w JavaScript przez michal_php Stary wyjadacz (13,700 p.)
0 głosów
1 odpowiedź 232 wizyt
pytanie zadane 26 lipca 2020 w JavaScript przez rob Bywalec (2,440 p.)

93,008 zapytań

141,975 odpowiedzi

321,256 komentarzy

62,350 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

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...