Redux React, how to set state, from form, inside a function - reactjs

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

Related

How to capture logged user in the array of users?

in my CRUD app, the user on the first screen enters their username and then can create, edit, and delete posts. I have an array of users and an array of posts (I'm using Redux). When a user creates a post, their username needs to appear in a sector within the div to identify who posted the post, but I still can't find a way to get the right user who is logged in to show in the div. How could I capture the specific user who is currently logged in?
I thought of doing something like: when the user typed their username and went to the posts screen, all posts would be linked to this user, but I don't know how to do that
postsSlice.js:
import { createSlice } from "#reduxjs/toolkit";
const postsSlice = createSlice({
name: "posts",
initialState: [],
reducers: {
addPost (state, action) {
state.push(action.payload);
},
editPost(state, action) {
const { id, title, content } = action.payload;
const existingPost = state.find((post) => post.id === id);
if (existingPost) {
existingPost.title = title
existingPost.content = content
}
},
postDeleted(state, action) {
const { id } = action.payload;
const existingPost = state.some((post) => post.id === id);
if (existingPost) {
return state.filter((post) => post.id !== id);
}
},
},
});
export const { addPost, editPost, postDeleted } = postsSlice.actions
export default postsSlice
usersSlice.js:
import { createSlice } from "#reduxjs/toolkit";
const userSlice = createSlice({
name: "user",
initialState: [],
reducers: {
saveUser (state, action) {
state.push(action.payload)
},
}
});
export const { saveUser, replaceUsers } = userSlice.actions;
export default userSlice
mainscreen.js:
import React, { useState, useEffect } from "react";
import "../_assets/App.css";
import "../_assets/mainscreen.css";
import { MdDeleteForever } from "react-icons/md";
import { FiEdit } from "react-icons/fi";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addPost } from '../redux/postsslice'
import Modal from "../components/modal.jsx";
import EditModal from '../components/editmodal.jsx';
function MainScreen() {
const dispatch = useDispatch();
const navigate = useNavigate();
const user = useSelector((state) => state.user)
const posts = useSelector((state) => state.loadPosts)
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [buttonGreyOut, setButtonGreyOut] = useState("#cccccc");
useEffect(() => {
if (title && content !== "") {
setButtonGreyOut("black");
} else {
setButtonGreyOut("#cccccc");
}
},[title, content]);
const handleSubmitSendPost = (e) => {
e.preventDefault();
dispatch(
addPost({
id: new Date(),
title,
content,
user
})
)
setTitle('')
setContent('')
};
const handleChangeTitle = (text) => {
setTitle(text);
};
const handleChangeContent = (text) => {
setContent(text);
};
const handleButton = (e) => {
e.preventDefault()
navigate("/")
}
const [openEditModal, setOpenEditModal] = useState();
const [openModal, setOpenModal] = useState();
console.log({ posts })
return (
<div className="containerMainScreen">
<button onClick={handleButton}>Back</button>
{openModal && <Modal deleteId={openModal} closeModal={setOpenModal} />}
{openEditModal && <EditModal editId={openEditModal} closeModal={setOpenEditModal} />}
<div className="bar">
<h1>Codeleap</h1>
</div>
<div className="boxPost">
<h2 style={{ fontWeight: 700 }}>What's on your mind?</h2>
<h2>Title</h2>
<form onSubmit={handleSubmitSendPost}>
<input
type="text"
placeholder="Hello World"
name="name"
value={title}
onChange={(e) => handleChangeTitle(e.target.value)}
></input>
<h2>Content</h2>
<textarea
placeholder="Content"
name="content"
value={content}
onChange={(e) => handleChangeContent(e.target.value)}
></textarea>
<button
className="createButton"
type="submit"
style={{ backgroundColor: buttonGreyOut }}
disabled={!title || !content}
>
CREATE
</button>
</form>
</div>
{posts.slice().reverse().map((post) => (
<div className="boxPost" key={post.id}>
<div className="bar">
<h1>{post.title}</h1>
<MdDeleteForever
className="icon"
onClick={() => {
setOpenModal(post.id);
}}
/>
<FiEdit
onClick={() => {
setOpenEditModal(post.id);
}}
style={{ color: "white", fontSize: "45px", paddingLeft: "23px" }}
/>
</div>
<div id="postowner">
<h3>#{user}</h3>
<br></br>
<textarea style={{ border: "none" }} value={post.content}></textarea>
</div>
</div>
))}
</div>
);
}
export default MainScreen;
Signup:
import React, {useState, useEffect} from "react";
import "../_assets/signup.css";
import "../_assets/App.css";
import { useDispatch } from 'react-redux';
import userSlice from '../redux/userslice';
import { useNavigate } from "react-router-dom";
function Signup() {
const navigate = useNavigate();
const dispatch = useDispatch();
const [name, setName] = useState('')
const [buttonGrey, setButtonGrey] = useState('#cccccc')
useEffect(() => {
if (name!== '') {
setButtonGrey("black")
}
else {
setButtonGrey('#cccccc')
}
}, [name])
const handleSubmitForm= (e) => {
e.preventDefault()
dispatch(userSlice.actions.saveUser(name));
navigate("/main")
}
const handleChangeName = (text) => {
setName(text)
}
return (
<div className="container">
<div className="LoginBox">
<form onSubmit={handleSubmitForm}>
<h2>Welcome to codeleap network</h2>
<text>Please enter your username</text>
<input type="text" name="name" value={name} onChange = {e => handleChangeName(e.target.value)} placeholder="Jane Doe" />
<div className="button">
<button type="submit" style={{backgroundColor: buttonGrey}} disabled={!name} >
ENTER
</button>
</div>
</form>
</div>
</div>
);
}
export default Signup;
You can store a current user and maintain a list of all users that ever "signed in".
user.slice.js
const initialState = {
currentUser: '',
users: [],
}
const userSlice = createSlice({
name: "user",
initialState,
reducers: {
saveUser(state, action) {
// person that just signed in
state.currentUser = action.payload;
// list of unique user names
state.users = Array.from(new Set([...state.users, action.payload]));
},
replaceUsers: (state, action) => action.payload
}
});
export const { saveUser } = userSlice.actions;
export default userSlice;
MainScreen.js
Add an author property to each added post, and access the post.author property when mapping the posts.
function MainScreen() {
const dispatch = useDispatch();
const { currentUser } = useSelector((state) => state.user); // <-- current user
const posts = useSelector((state) => state.loadPosts);
...
const handleSubmitSendPost = (e) => {
e.preventDefault();
dispatch(
addPost({
id: uuidV4(),
title,
content,
author: currentUser // <-- current user to post
})
);
setTitle("");
setContent("");
};
...
if (currentUser === "") {
return <Navigate to="/" />;
} else {
return (
<div className="containerMainScreen">
...
{posts.map((post) => (
<div className="boxPost" key={post.id}>
<div className="bar">
<h1>{post.title}</h1>
<MdDeleteForever
className="icon"
onClick={() => {
setOpenModal(post.id);
}}
/>
<FiEdit
onClick={() => {
setOpenEditModal(post.id);
}}
style={{
color: "white",
fontSize: "45px",
paddingLeft: "23px"
}}
/>
</div>
<div id="postowner">
<h3>#{post.author}</h3> // <-- user in post object
<br></br>
<textarea
style={{ border: "none" }}
value={post.content}
></textarea>
</div>
</div>
))}
</div>
);
}
}
export default MainScreen;

Get method is being called infinitely

I am fetching the data to populate it on the form but when I try to edit the data in the input, the input value will return to its original value and it is because of the get method that is infinitely render on the component. I really need your eyes to see something that have missed or missed up. Thanks in advance y'all.
fetch method
import * as api from '../api/profile';
export const getProfile = () => async (dispatch) => {
try {
const { data } = await api.fetchProfile();
dispatch({ type: 'FETCH_ALL', payload: data });
} catch (error) {
console.log(error.message);
}
}
Profile container
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getProfile } from '../../../actions/profile'; //fetch method
import Profile from './Profile';
function Index() {
const dispatch = useDispatch();
const posts = useSelector((state) => state.posts);
const currentId = useState(null);
useEffect(() => {
dispatch(getProfile());
}, [currentId, dispatch]);
return (
<div className="custom-container">
{posts.map((profile) => (
<div key={profile._id}>
<Profile profile={profile} currentId={currentId} />
</div>
))}
</div>
);
}
export default Index;
Profile form component
import './Profile.css';
import { React, useState, useEffect } from 'react';
import Button from 'react-bootstrap/Button';
import { TextField } from '#material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { updateProfile } from '../../../actions/profile';
const Profile = ({ profile, currentId }) => {
const dispatch = useDispatch();
currentId = profile._id;
const [postData, setPostData] = useState(
{
profile: {
name: "",
description: "",
email: "",
number: "",
}
}
);
const post = useSelector((state) => currentId ? state.posts.find((p) => p._id === currentId) : null);
useEffect(() => {
if(post) setPostData(post);
}, [post])
const handleSubmit = (e) => {
e.preventDefault();
if(currentId) {
dispatch(updateProfile(currentId, postData));
}
}
return (
<form autoComplete="off" noValidate className="form" onSubmit={handleSubmit}>
<TextField
id="name"
name="name"
className="name"
label="Full Name"
variant="outlined"
value={postData.profile.name}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, name: e.target.value}})}
/>
<TextField
id="outlined-multiline-static"
label="Multiline"
multiline
rows={4}
variant="outlined"
size="small"
className="mb-3"
name="description"
value={postData.profile.description}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, description: e.target.value}})}
fullWidth
/>
<TextField
id="email"
label="Email"
variant="outlined"
size="small"
className="mb-3"
name="email"
value={postData.profile.email}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, email: e.target.value}})}
/>
<TextField
id="phone"
label="Phone Number"
variant="outlined"
size="small"
name="phone"
value={postData.profile.number}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, number: e.target.value}})}
/>
<Button variant="light" type="submit" className="Save">Save</Button>
</form>
);
}
export default Profile;
In useEffect you have passed like this , in Profile Container
useEffect(() => {
dispatch(getProfile());
}, [currentId, dispatch]);
in params you have passed dispatch also , so it will call dispatch every time dispatch runs , so it is called infinite times , remove it
It might be because of useEffect.
useEffect(() => {
if(post) setPostData(post);
}, [post])
The post will be a different object each time. check for post property in useEffect dependency like:
useEffect(() => {
if(post) setPostData(post);
}, [post.profile.description])
Also, why are you changing prop value below in Profile component?
currentId = profile._id

state not updating in react hooks

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 };

Fix this react memory leak

I need to fix a memory leak in my app but Im not sure how to. I have a component that uses a modal and I get the error when I am adding an item. The modal is reusable and I use it in other components as well. This is the main component:
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Card, Select, Form, Button } from 'antd';
import Table from 'components/Table';
import Modal from '../Modal';
import styles from '../index.module.scss';
const { Item } = Form;
const { Option } = Select;
const PersonForm = ({ details, form }) => {
const [modalVisible, setModalVisible] = useState(false);
const [name, setName] = useState(
details?.name ? [...details?.name] : []
);
useEffect(() => {
form.setFieldsValue({
name: name || [],
});
}, [form, details, name]);
const addName = values => {
setName([...name, values]);
setModalVisible(false);
};
const removeName = obj => {
setName([...name.filter(i => i !== obj)]);
};
const cancelModal = () => {
setModalVisible(false);
};
return (
<div>
<Card
title="Names
extra={
<Button type="solid" onClick={() => setModalVisible(true)}>
Add Name
</Button>
}
>
<Table
tableData={name}
dataIndex="name"
removeName={removeName}
/>
</Card>
<Item name="name">
<Modal
title="Add Name"
fieldName="name"
onSubmit={addName}
visible={modalVisible}
closeModal={cancelModal}
/>
</Item>
</div>
);
};
PersonForm.propTypes = {
details: PropTypes.instanceOf(Object),
form: PropTypes.instanceOf(Object),
};
PersonForm.defaultProps = {
form: null,
details: {},
};
export default PersonForm;
And this is the modal component:
import React from 'react';
import PropTypes from 'prop-types';
import { Input, Form } from 'antd';
import Modal from 'components/Modal';
import LocaleItem from 'components/LocaleItem';
const { Item } = Form;
const FormModal = ({ visible, closeModal, onSubmit, fieldName, title }) => {
const [form] = Form.useForm();
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 15 },
};
const addItem = () => {
form
.validateFields()
.then(values => {
onSubmit(values, fieldName);
form.resetFields();
closeModal(fieldName);
})
.catch(() => {});
};
const canceledModal = () => {
form.resetFields();
closeModal(fieldName);
};
return (
<Modal
onSuccess={addItem}
onCancel={canceledModal}
visible={visible}
title={title}
content={
<Form {...layout} form={form}>
<Item
name="dupleName"
label="Name:"
rules={[
{
required: true,
message: 'Name field cannot be empty',
},
]}
>
<Input placeholder="Enter a name" />
</Item>
</Form>
}
/>
);
};
FormModal.propTypes = {
visible: PropTypes.bool.isRequired,
closeModal: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
fieldName: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
};
FormModal.defaultProps = {};
export default FormModal;
I get a memory leak when I am in the test file when adding items in the modal. Can someone point out why this is happening and how to fix this? Thanks
Remove closeModal and form.resetFields from addItem function.
const addItem = () => {
form
.validateFields()
.then(values => {
onSubmit(values, fieldName); // when this onSubmit resolves it closes the modal, therefor these two lines below will be executed when component is unmounted, causing the memory leak warning
form.resetFields();
closeModal(fieldName);
})
.catch(() => {});
};
// instead maybe just:
const [form] = Form.useForm();
<Modal onOk={form.submit}>
<Form form={form}>
<Form.Item name="foo" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
</Modal>
Also, as far as I know you don't need to call form.validateFields as Ant Design's Form would do that automatically if rules are set in the Form.Item's.

Dispatch redux not executed

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));
}
);
};
}

Resources