I am trying to do Firebase auth in a NextJS project but I am getting the following error:
TypeError: Cannot destructure property 'currentUser' of '(0 , _lib_contexts_AuthContext__WEBPACK_IMPORTED_MODULE_6__.useAuth)(...)' as it is undefined.
9 | export default function Login() {
10 | // const router = useRouter();
> 11 | const { currentUser, setCurrentUser, login, logout } = useAuth();`
This is my AuthContext.js file:
import React, { createContext, useContext, useEffect, useState } from 'react';
import { signOut, signInWithEmailAndPassword, onAuthStateChanged } from 'firebase/compat/auth';
import { auth } from '../../core/firebase';
export const AuthContext = createContext();
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, user => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe();
}, []);
function login(email, password) {
return signInWithEmailAndPassword(auth, email, password);
}
async function logout() {
setCurrentUser(null);
await signOut(auth);
console.log('logout');
return;
}
function resetPassword(email) {
return auth.sendPasswordResetEmail(email);
}
const value = {
currentUser,
setCurrentUser,
login,
logout,
resetPassword,
};
return <AuthContext.Provider value={value}>{!loading && children}</AuthContext.Provider>;
}
export function useAuth() {
return useContext(AuthContext);
}
This is my login.jsx page, somehow it is not getting any of the exported values from the AuthContext file. Every example I have seen uses this destructuring format for getting the values form useAuth() so I am not sure why I am getting this error now
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { useEffect, useState } from 'react';
import { useAuth } from '../lib/contexts/AuthContext';
export default function Login() {
const { currentUser, setCurrentUser, login, logout } = useAuth();
async function handleLogin(email, password) {
try {
await login(email, password);
} catch (err) {
console.log(err);
}
}
function handleLogout() {
logout();
}
return (
<>
<div>Currently logged in: {currentUser?.email}</div>
<Formik
initialValues={{
email: '',
password: '',
}}
validationSchema={Yup.object({
email: Yup.string().email('Invalid email address').required('Required'),
password: Yup.string().required('No password provided.'),
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
setSubmitting(false);
}, 400);
handleLogin(values.email, values.password);
}}>
<Form>
<label className='border border-black' htmlFor='Email'>
Email
</label>
<Field className='border border-black' id='email' name='email' />
<ErrorMessage component='a' className='border border-black' name='email' />
<label className='border border-black' htmlFor='Email'>
Password
</label>
<Field className='border border-black' id='password' name='password' />
<ErrorMessage component='a' className='border border-black' name='password' />
<div className='mt-8'>
<button
disabled={currentUser ? true : false}
type='submit'
className='border border-black'>
Login
</button>
</div>
</Form>
</Formik>
<div>
<button
disabled={currentUser ? false : true}
className='border border-black'
onClick={handleLogout}>
Logout
</button>
</div>
</>
);
}
I am wrapping the _app.js return in the AuthProvider which seems to be the most common cause for this error.
import '../styles/globals.css';
import { AuthProvider } from '../lib/contexts/AuthContext';
function MyApp({ Component, pageProps }) {
return (
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
);
}
export default MyApp;
Related
i am using react-router-dom v6 this code generating above error message ..please help me to solve the error ... backend is working fine ..i think this error is coming from fronted .... it works with postman ..
i am following a older tutorial ... now i installed new version of react-router-dom ... please help me out
this is ResetPassword.js file
import React, { Fragment, useState, useEffect } from "react";
import "./ResetPassword.css";
import Loader from "../layout/Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { clearErrors, resetPassword } from "../../actions/userAction";
import { useAlert } from "react-alert";
import MetaData from "../layout/MetaData";
import LockOpenIcon from "#material-ui/icons/LockOpen";
import LockIcon from "#material-ui/icons/Lock";
const ResetPassword = ({ history, match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const { error, success, loading } = useSelector(
(state) => state.forgotPassword
);
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const resetPasswordSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("password", password);
myForm.set("confirmPassword", confirmPassword);
dispatch(resetPassword(match.params.token, myForm));
};
useEffect(() => {
if (error) {
alert.error(error);
dispatch(clearErrors());
}
if (success) {
alert.success("Password Updated Successfully");
history.push("/login");
}
}, [dispatch, error, alert, history, success]);
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
<MetaData title="Change Password" />
<div className="resetPasswordContainer">
<div className="resetPasswordBox">
<h2 className="resetPasswordHeading">Update Profile</h2>
<form
className="resetPasswordForm"
onSubmit={resetPasswordSubmit}
>
<div>
<LockOpenIcon />
<input
type="password"
placeholder="New Password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<LockIcon />
<input
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<input
type="submit"
value="Update"
className="resetPasswordBtn"
/>
</form>
</div>
</div>
</Fragment>
)}
</Fragment>
);
};
export default ResetPassword;
And The backend code is here
export const resetPassword = (token, passwords) => async (dispatch) => {
try {
dispatch({ type: RESET_PASSWORD_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.put(
`/api/v1/password/reset/${token}`,
passwords,
config
);
dispatch({ type: RESET_PASSWORD_SUCCESS, payload: data.success });
} catch (error) {
dispatch({
type: RESET_PASSWORD_FAIL,
payload: error.response.data.message,
});
}
};
Thank you
In react-router-dom#6 the Route component API changed significantly. There are no longer any route props (i.e. no match or history props) all replaced by React hooks. The history object was replaced by a navigate function via the useNavigate hook, and route path params are accessible via the useParams hook.
Example:
import { useNavigate, useParams } from 'react-router-dom';
const ResetPassword = () => {
const navigate = useNavigate(); // <-- access navigate function
const { token } = useParams(); // <-- access token path parameter
...
const resetPasswordSubmit = (e) => {
...
dispatch(resetPassword(token, myForm)); // <-- use token param here
};
useEffect(() => {
...
if (success) {
alert.success("Password Updated Successfully");
navigate("/login"); // <-- call navigate here
}
}, [dispatch, error, alert, navigate, success]);
I am learning Redux. I cannot figure out how to set state.
I need to set state (I'm assuming with useDispatch) by using a login form. On the component Fake1, I am able to console.log the "user" passed with useSelector. If i hardcode a change in state on user.js ({ i.e., username: "beanbag", password: "122345" }), the change in state appears on Fake1, telling me that the mechanics of my setup are good, and that the problem is that state is not being set inside loginOnSubmit().
My code:
const initialStateValue = { username: "", password: "" };
export const userSlice = createSlice({
name: "user",
initialState: { value: initialStateValue },
reducers: {
login: (state, action) => {
state.value = action.payload;
},
logout: (state) => {
state.value = initialStateValue;
},
},
});
export const { login, logout } = userSlice.actions;
export default userSlice.reducer;
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import Visibility from "#mui/icons-material/Visibility";
import VisibilityOff from "#mui/icons-material/VisibilityOff";
import InputAdornment from "#mui/material/InputAdornment";
import IconButton from "#mui/material/IconButton";
import Input from "#mui/material/Input";
import Button from "#mui/material/Button";
import LoginIcon from "#mui/icons-material/Login";
import AddCircleOutlineIcon from "#mui/icons-material/AddCircleOutline";
import Stack from "#mui/material/Stack";
import "./LoginForm.css";
import { useDispatch } from "react-redux";
import { login } from "../features/user";
function LoginForm() {
const [user, setUser] = useState(null);
const [loginUsername, setLoginUsername] = useState("");
const [loginError, setLoginError] = useState([]);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [values, setValues] = useState({
password: "",
showPassword: false,
});
const dispatch = useDispatch();
const navigate = useNavigate();
const handleChange = (prop) => (event) => {
setValues({ ...values, [prop]: event.target.value });
};
const handleClickShowPassword = () => {
setValues({
...values,
showPassword: !values.showPassword,
});
};
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
// useEffect(() => {
// fetch("/authorize_user")
// .then((res) => res.json())
// .then(setUser);
// }, []);
const loginOnSubmit = (e) => {
e.preventDefault();
const newUser = {
username: loginUsername,
password: values.password,
};
// dispatch(login({username: loginUsername, password: values.password}))
fetch("/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newUser),
}).then((res) => {
if (res.ok) {
res.json().then((newUser) => {
setUser(newUser);
setIsAuthenticated(true);
setLoginUsername("");
dispatch(login({ newUser }));
navigate("/fake1");
});
} else {
res.json().then((json) => setLoginError(json.error));
}
});
};
const handleSignupRoute = () => {
navigate("/signup");
};
return (
<form onSubmit={loginOnSubmit}>
<div>
<br></br>
<Input
className="test1"
value={loginUsername}
onChange={(e) => setLoginUsername(e.target.value)}
type="text"
label="Username"
placeholder="Username"
/>
<br></br>
<br></br>
<Input
id="standard-adornment-password"
type={values.showPassword ? "text" : "password"}
value={values.password}
// onChange={(e) => setValues(e.target.value)}
onChange={handleChange("password")}
placeholder="Password"
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
>
{values.showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
}
/>
<br></br>
<br></br>
<br></br>
<div className="test2">
<Stack direction="row" spacing={2}>
<Button
type="submit"
variant="outlined"
endIcon={<LoginIcon />}
className="btn-login"
>
Login
</Button>
<Button
onClick={handleSignupRoute}
variant="outlined"
endIcon={<AddCircleOutlineIcon />}
className="btn-signup"
>
Signup
</Button>
</Stack>
<br></br>
<br></br>
</div>
</div>
</form>
);
}
export default LoginForm;
import React from 'react'
import {useSelector} from 'react-redux'
const Fake1 = () => {
const user = useSelector(state => state.user.value)
console.log(user)
return (
<div>Fake1</div>
)
}
export default Fake1
I'm trying to figure out the best way to create a global state variable that will hold a firebase authentication user id.
For example the below code would check if a user is logged in and then send them to welcome page if successful.
But I also need to setup up private routes on a different file, I want to be able to share the getId state. I read that useContext can do this but unsure how to implement it. Please advise, thanks
const [getId, setId] = useState("");
const login = async ( id ) => {
return setId(id);
};
firebase.auth().onAuthStateChanged((user) => {
if (user) {
login(user.uid).then(() => {
history.push("/welcome");
});
} else {
history.push("/");
}
});
const PrivateRoute = ({ getId, component: Component, ...rest }) => (
<Route
{...rest}
component={(props) =>
getId ? (
<div>
<Component {...props} />
</div>
) : (
<Redirect to="/" />
)
}
/>
);
I'll give you my example to have an Auth Context. Here are the parts:
The _app.js file:
import '../styles/globals.scss'
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'
import Header from '../components/Header'
import Footer from '../components/Footer'
import { AuthProvider } from '../contexts/AuthContext'
import { CartProvider } from '../contexts/CartContext'
import { ThemeProvider } from '#material-ui/core'
import theme from '../styles/theme'
export default function App({ Component, pageProps }) {
const router = useRouter()
return(
<AnimatePresence exitBeforeEnter>
<CartProvider>
<AuthProvider>
<ThemeProvider theme={theme}>
<Header />
<motion.div key={router.pathname} className="main">
<Component { ...pageProps } />
<Footer />
</motion.div>
</ThemeProvider>
</AuthProvider>
</CartProvider>
</AnimatePresence>
)
}
The item of significance is the <AuthProvider> component. That's where the context is wrapped.
The AuthContent.js file:
import { createContext, useContext, useEffect, useState } from 'react'
import { auth } from '../firebase'
const AuthContext = createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
function signOut() {
return auth.signOut();
}
function signUp(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function getUser() {
return auth.currentUser
}
function isAdmin() {
return auth.currentUser.getIdTokenResult()
.then((idTokenResult) => {
if (!!idTokenResult.claims.admin) {
return true
} else {
return false
}
})
}
function isEditor() {
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
getUser,
login,
signOut,
signUp
}
return (
<AuthContext.Provider value={value}>
{ !loading && children }
</AuthContext.Provider>
)
}
This is where the state is stored and accessed, including all of the helpers (signup, signout, login, etc).
How to use it:
import { Button, Card, CardHeader, CardContent, Link, TextField, Typography } from '#material-ui/core'
import { motion } from 'framer-motion'
import { useRef, useState } from 'react'
import { useAuth } from '../contexts/AuthContext'
import { useRouter } from 'next/router'
export default function SignupForm() {
const router = useRouter()
const { signUp } = useAuth()
const [state, setState] = useState({
email: "",
password: "",
passwordConfirm: ""
})
const [error, setError] = useState("")
function handleForm(e) {
setState({
...state,
[e.target.name]: e.target.value
})
}
async function handleSubmit(e) {
if (state.password !== state.passwordConfim) {
setError("Passwords do not match")
}
await signUp(state.email, state.password)
.catch(err => console.log(JSON.stringify(err)) )
router.push("/account")
}
return(
<motion.div>
<Card >
<CardHeader title="Header" />
<CardContent>
<TextField label="email" name="email" variant="outlined" onChange={ handleForm } />
<TextField label="password" name="password" type="password" variant="outlined" onChange={ handleForm } />
<TextField label="Password Confirmation" name="passwordConfirm" type="password" variant="outlined" onChange={ handleForm } />
{error && <Alert severity="error" variant="filled" >{error}</Alert>}
<Button onClick={ handleSubmit }>
<Typography variant="button">Sign Up</Typography>
</Button>
</CardContent>
</Card>
</motion.div>
)
}
You import { useAuth } from your context (I usually put mine in a context folder) and then you can invoke instances of the variables inside the component by destructuring (e.g. const { currentUser, login } = useAuth())
i have created a login form where user need to input his email id and OTP. below is my code -
import { useState } from 'react';
import axios from '../api/axios';
const useLogin = () => {
const [user, setUser] = useState(false);
const auth = async (value, OTP) => {
let config = {
method: 'POST',
url: '/api/user/generateToken',
headers: {
Authorization: 'value'
},
data: {
username: value,
password: OTP
}
};
try {
const response = await axios(config);
if (response.data.Status === "Failure") {
throw response.data.Message;
} else {
setUser(true);
return { status: response.data.Status, isAuth: user }
}
} catch (err) {
setUser(false);
return { status: undefined, message: err, isAuth: user };
}
}
return { auth, user };
}
export default useLogin
Everything is working fine here only problem is when i'm calling this function in my component i'll receive isAuth always false. Below is my component code -
import React, { Fragment, useRef, useEffect, useState } from 'react';
import { useLocation, useHistory } from "react-router-dom";
import { css } from "#emotion/core";
import ScaleLoader from "react-spinners/ScaleLoader";
import '../css/login.css';
import '../css/common.css';
import logo from '../assets/engageLogo.png';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import useLogin from './../hooks/useOTP';
const override = css`
display: block;
margin: 0 auto;
border-color: #fff;
`;
const OTP = () => {
const [loading, setLoading] = useState(false);
const [color] = useState("#ffffff");
const [APIResponse, setAPIResponse] = useState(false);
const [APIMessage, setAPIMessage] = useState('')
const login = useLogin();
const location = useLocation();
const history = useHistory();
const inputRef = useRef();
const readRef = useRef();
const buttonRef = useRef();
const schema = Yup.object({
otp: Yup.string().required("OTP is Required")
});
const handleChangeError = () => {
return setAPIResponse(false)
}
const {
handleSubmit,
handleChange,
handleBlur,
touched,
errors,
} = useFormik({
initialValues: {
otp: "",
},
validationSchema: schema,
onSubmit: (values) => {
console.log(JSON.stringify(values));
buttonRef.current.disabled = true;
setLoading(true);
const loginCall = login.auth(location.state.email, values.otp);
loginCall.then(response => {
if (response.status === undefined || response.status === null) {
setLoading(false);
buttonRef.current.disabled = false;
setAPIResponse(true)
setAPIMessage(response.message)
} else {
setLoading(false);
history.push({
pathname: '/dashboard',
state: { email: values.email }
});
}
})
},
});
useEffect(() => {
inputRef.current.focus();
readRef.current.value = location.state.email;
}, [location])
return <Fragment>
<div className="centered-form">
<div className="centered-form__box">
<div className="mb-3 text-center">
<img src={logo} className="img-fluid" width="150" alt="Logo" />
</div>
<form onSubmit={handleSubmit} noValidate>
<div className="mb-3">
<label htmlFor="readEmail" className="form-label">Email</label>
<input
type="text"
name="readEmail"
id="readEmail"
ref={readRef}
className="form-control" readOnly />
</div>
<div className="mb-3">
<label htmlFor="otp" className="form-label">OTP</label>
<input
type="text"
name="otp"
id="otp"
ref={inputRef}
onChange={(e) => { handleChange(e); handleChangeError(e) }}
onBlur={handleBlur}
className="form-control" placeholder="Enter OTP" required />
{touched.otp && errors.otp
? <div className="invalid-feedback">Please enter valid OTP</div>
: null}
{APIResponse
? <div className="invalid-feedback">{APIMessage}</div>
: null}
</div>
<div className="d-grid gap-2">
<button ref={buttonRef} className="btn btn-main">{loading ?
<ScaleLoader color={color} loading={loading} css={override} height={15} /> : <span>Login</span>}</button>
</div>
</form>
</div>
</div>
</Fragment>
}
export default OTP
in response of loginCall i'll always get isAuth: false.
I want to use isAuth for protecting my routes. Just to check whether user has logged in or not.
why setUser is not updating the value here.
thanks in advance...
That's because by the time you returning your isAuth value the new user value is not set yet. you need to know that React setState is asynchronous function.
just use the the boolean itself directly like this:
setUser(true);
return { status: response.data.Status, isAuth: true }
or in case of a rejection:
setUser(false);
return { status: undefined, message: err, isAuth: false };
I'm trying to submit a form when a user signUp. When the submit button clicked an action creator should executed to start an asynchronous action but actually the submit is not triggered and the action creator is not launched.
actions.ts:
import { ActionTypes } from "./types";
import { SignUpUser, User } from "../apis/authentication";
import { AxiosError } from "axios";
import { Dispatch } from "redux";
export interface ReturnedUser {
username: string;
}
export interface SignUpSuccessAction {
type: ActionTypes.SucceedSignUp;
payload: ReturnedUser;
}
export interface SignUpFailAction {
type: ActionTypes.FailSignUp;
payload: string;
}
export interface SignUpStartAction {
type: ActionTypes.StartSignUp;
}
const signUpStarted = (): SignUpStartAction => ({
type: ActionTypes.StartSignUp
});
const signUpSucceeded = (user: ReturnedUser): SignUpSuccessAction => ({
type: ActionTypes.SucceedSignUp,
payload: user
});
const signUpFailed = (error: string): SignUpFailAction => ({
type: ActionTypes.FailSignUp,
payload: error
});
export const signUpFetch = (user: User) => {
return async (dispatch: Dispatch) => {
dispatch(signUpStarted());
SignUpUser(user).then(
(response: any) => {
const { username } = response;
return dispatch(signUpSucceeded(username));
},
(error: AxiosError) => {
let errorMessage = "Internal Server Error";
if (error.response) {
errorMessage = error.response.data;
}
return dispatch(signUpFailed(errorMessage));
}
);
};
};
reducers/reducer.ts:
import { Action, ActionTypes } from "../actions";
export const SignUpReducer = (state = {}, action: Action) => {
switch (action.type) {
case ActionTypes.SucceedSignUp:
return { ...state, user: action.payload };
case ActionTypes.FailSignUp:
return { ...state, error: action.payload };
default:
return state;
}
};
reducers/index.ts:
import { SignUpReducer } from "./signUp";
import { combineReducers } from "redux";
export const reducer = combineReducers({
signUp: SignUpReducer
});
index.tsx:
import React from "react";
import ReactDOM from "react-dom";
import SignUp from "./containers/Signup/SignUp";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { reducer } from "./reducers/index";
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
const App = () => <SignUp />;
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
SignUp.tsx:
import React, { useState } from "react";
import Button from "#material-ui/core/Button";
import { connect } from "react-redux";
import { Form, Field } from "react-final-form";
import { makeStyles, Theme, createStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import CardWrapper from "../../components/CardWrapper";
import PasswordField from "../../components/Password";
import TextField from "../../components/TextField";
import { validate, submit } from "./validation";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
container: {
padding: 16,
margin: "auto",
maxWidth: "100%",
flexGrow: 1
},
paper: {
padding: 16
},
item: {
marginTop: 16
}
})
);
const SignUp = () => {
const classes = useStyles();
const [showPassword, setPassword] = useState(false);
const handleClickShowPassword = () => {
setPassword(!showPassword);
};
const handleMouseDownPassword = (
event: React.MouseEvent<HTMLButtonElement>
) => {
event.preventDefault();
};
return (
<div className={classes.container}>
<Form
onSubmit={submit}
validate={validate}
render={({ handleSubmit, form, submitting, pristine }) => (
<form onSubmit={handleSubmit}>
<CardWrapper title='SignUp Form'>
<Grid container justify='center' spacing={3}>
<Grid item md={12}>
<Field fullWidth required name='username'>
{props => (
<TextField
label='Username'
type='text'
value={props.input.value}
onChange={props.input.onChange}
onBlur={props.input.onBlur}
meta={props.meta}
fullWidth={true}
/>
)}
</Field>
</Grid>
<Grid item md={12}>
<Field fullWidth required name='email'>
{props => (
<TextField
label='Email'
type='email'
value={props.input.value}
onChange={props.input.onChange}
onBlur={props.input.onBlur}
meta={props.meta}
fullWidth={true}
/>
)}
</Field>
</Grid>
<Grid item md={12}>
<Field fullWidth required name='password'>
{props => (
<PasswordField
value={props.input.value}
handleChange={props.input.onChange}
showPassword={showPassword}
handleClickShowPassword={handleClickShowPassword}
handleMouseDownPassword={handleMouseDownPassword}
fullWidth={true}
onBlur={props.input.onBlur}
meta={props.meta}
/>
)}
</Field>
</Grid>
<Grid item className={classes.item}>
<Button
type='button'
variant='contained'
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</Button>
</Grid>
<Grid item className={classes.item}>
<Button
variant='contained'
color='primary'
type='submit'
disabled={submitting || pristine}
>
Submit
</Button>
</Grid>
</Grid>
</CardWrapper>
</form>
)}
/>
</div>
);
};
export default connect()(SignUp);
validation.ts:
interface SignUpValues {
email: string;
password: string;
username: string;
}
const submit = (values: SignUpValues) => {
const user = {
username: values.username,
email: values.email,
password: values.password
};
return signUpFetch(user);
};
export { submit };
I find a similar question posted about the same issue described by Redux Dispatch Not Working in Action Creator but the answer does not fix my problem. Does I make something wrong when linking the different component with redux?
It wont dispatch because in component You didnt dispatch Your function
return signUpFetch(user)
Instead Connect the component with Redux and dispatch the function
in Index.tsx
import { connect } from 'react-redux';
const mapDispatchToProps = {
submit
};
export default connect(null, mapDispatchToProps)(SignUp);
And access it with
this.props.submit
Add dispatch in Submit function
const submit = (values: SignUpValues) =>(dispatch, getState)=> {
const user = {
username: values.username,
email: values.email,
password: values.password
};
return dispatch(signUpFetch(user));
};
Whenever you need to update redux state, dispatch the function from where it is also called and also in the actions.
You need to connection the component to the store when you do the dispatch:
import { connect } from 'react-redux';
const submit = (values: SignUpValues) => {
const user = {
username: values.username,
email: values.email,
password: values.password
};
return this.props.signUpFetch(user);
};
export const connectedSubmit = connect(null, {signUpFetch})(submit);
import { validate, connectedSubmit as submit } from "./validation";
And also you can just return at SignUpUser
export const signUpFetch = (user: User) => {
return async (dispatch: Dispatch) => {
dispatch(signUpStarted());
return SignUpUser(user).then(
(response: any) => {
const { username } = response;
dispatch(signUpSucceeded(username));
},
(error: AxiosError) => {
let errorMessage = "Internal Server Error";
if (error.response) {
errorMessage = error.response.data;
}
dispatch(signUpFailed(errorMessage));
}
);
};
}