i want to implement update user password form using react-router-dom v6 but this code is not working..
please please.. put your suggestion or explain me about my mistakes on this code.
userReducer.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_RESET,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const profileReducer = (state = {}, action) => {
switch (action.type) {
case UPDATE_PASSWORD_REQUEST:
return {
...state,
loading: true,
};
case UPDATE_PASSWORD_SUCCESS:
return {
...state,
loading: false,
isUpdated: action.payload,
};
case UPDATE_PASSWORD_FAIL:
return {
...state,
loading: false,
error: action.payload,
};
case UPDATE_PASSWORD_RESET:
return {
...state,
isUpdated: false,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
userAction.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const updatePassword = (passwords) => async (dispatch) => {
try {
dispatch({ type: UPDATE_PASSWORD_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.put(
`/api/v1/password/update`,
passwords,
config
);
dispatch({ type: UPDATE_PASSWORD_SUCCESS, payload: data.success });
} catch (error) {
dispatch({
type: UPDATE_PASSWORD_FAIL,
payload: error.response.data.message,
});
}
};
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
store.js
import {createStore,combineReducers,applyMiddleware} from 'redux';
import thunk from "redux-thunk";
import {composeWithDevTools} from "redux-devtools-extension";
import { profileReducer } from './Reducers/userReducer';
const reducer = combineReducers({
profile:profileReducer,
})
let initialState = {};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
UpdatePassword.js
import React, { Fragment, useState, useEffect } from "react";
import "./UpdatePassword.css";
import Loader from "../Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { clearErrors, updatePassword } from "../../Actions/userAction";
import { UPDATE_PASSWORD_RESET } from "../../Constants/userConstant";
import {useNavigate} from 'react-router-dom'
const UpdatePassword = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { error, isUpdated, loading } = useSelector((state) => state.profile);
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const updatePasswordSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("oldPassword", oldPassword);
myForm.set("newPassword", newPassword);
myForm.set("confirmPassword", confirmPassword);
dispatch(updatePassword(myForm));
};
useEffect(() => {
if (error) {
alert(error);
dispatch(clearErrors());
}
if (isUpdated) {
alert("Profile Updated Successfully");
navigate("/account");
dispatch({
type: UPDATE_PASSWORD_RESET,
});
}
}, [dispatch, error, isUpdated]);
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
{/* <MetaData title="Change Password" /> */}
<div className="updatePasswordContainer">
<div className="updatePasswordBox">
<h2 className="updatePasswordHeading">Update Profile</h2>
<form
className="updatePasswordForm"
onSubmit={updatePasswordSubmit}
>
<div className="loginPassword">
<input
type="password"
placeholder="Old Password"
required
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="New Password"
required
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<input
type="submit"
value="Change"
className="updatePasswordBtn"
/>
</form>
</div>
</div>
</Fragment>
)}
</Fragment>
);
};
export default UpdatePassword;
i want to make a form where user update user password.but due to any mistake this form is not working...
Related
I am trying to check if the user is logged in or not in Register.js component but I am getting undefined values of auth state in it. Please look into it if you can find where I am doing wrong. I have tried doing a lot of changes still I was not able to get it right.
Thank you for all of your help.
Ignore the context files
App.js
import { useEffect } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import "./App.css";
import { Navbar } from "./components/layout/Navbar";
import { Home } from "./components/pages/Home";
import { MyList } from "./components/favorites/MyList";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
import { Provider } from "react-redux";
import store from "./store";
import { loadUser } from "./actions/auth";
import { setAuthToken } from "./utils/setAuthToken";
if (localStorage.token) {
setAuthToken(localStorage.token);
}
function App() {
useEffect(() => {
store.dispatch(loadUser());
}, []);
return (
<Provider store={store}>
<>
<div>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/mylist' element={<MyList />} />
<Route path='/signin' element={<Login />} />
<Route path='/signup' element={<Register />} />
</Routes>
</div>
</>
</Provider>
);
}
export default App;
Register.js
import React, { useContext, useEffect, useState } from "react";
import { Link, Navigate, useNavigate } from "react-router-dom";
import { connect } from "react-redux";
import { register } from "../../actions/auth";
import axios from "axios";
const Register = ({ register, isAuthenticated, history, location, auth }) => {
const navigate = useNavigate();
const [user, setUser] = useState({
name: "",
email: "",
password: "",
password2: "",
});
const { name, email, password, password2 } = user;
const onFormDataChange = (e) => {
setUser({
...user,
[e.target.name]: e.target.value,
});
};
const onFormSubmit = async (e) => {
e.preventDefault();
register({ name, email, password });
};
console.log(auth);
console.log(isAuthenticated);
return (
<div>
<h1>Sign Up</h1>
<form action='' onSubmit={onFormSubmit}>
<div className='form-group'>
<label htmlFor='name'>Name</label>
<input
type='text'
name='name'
value={name}
onChange={onFormDataChange}
/>
</div>
<div className='form-group'>
<label htmlFor='email'>Email</label>
<input
type='email'
name='email'
value={email}
onChange={onFormDataChange}
/>
</div>
<div className='form-group'>
<label htmlFor='password'>Password</label>
<input
type='password'
name='password'
value={password}
onChange={onFormDataChange}
/>
</div>
<div className='form-group'>
<label htmlFor='password2'>Password</label>
<input
type='password'
name='password2'
value={password2}
onChange={onFormDataChange}
/>
</div>
<input type='submit' value='Signup' />
<p>
Already have an account? <Link to='/signin'>Sign In</Link>
</p>
</form>
</div>
);
};
const mapStateToProps = (state) => {
const { isAuthenticated } = state.auth.isAuthenticated;
const { auth } = state.auth;
return {
isAuthenticated,
};
};
export default connect(mapStateToProps, { register })(Register);
index.js
import { combineReducers } from "redux";
import auth from "./auth";
export default combineReducers({
auth,
});
/auth/types.js
export const REGISTER_SUCCESS = "REGISTER_SUCCESS";
export const REGISTER_FAIL = "REGISTER_FAIL";
export const USER_LOADED = "USER_LOADED";
export const AUTH_ERROR = "AUTH_ERROR";
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN_FAIL = "LOGIN_FAIL";
export const LOGOUT = "LOGOUT";
export const CLEAR_ERRORS = "CLEAR_ERRORS";
actions/auth.js
import axios from "axios";
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
} from "./types";
import { setAuthToken } from "../utils/setAuthToken";
export const loadUser = () => async (dispatch) => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get("/api/auth");
dispatch({
type: USER_LOADED,
payload: res.data,
});
} catch (error) {
dispatch({
type: AUTH_ERROR,
});
}
};
export const register =
({ name, email, password }) =>
async (dispatch) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify({ name, email, password });
try {
const res = await axios.post("/api/users", body, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (error) {
dispatch({
type: REGISTER_FAIL,
});
}
};
export const login = (email, password) => async (dispatch) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify({ email, password });
try {
const res = await axios.post("/api/auth", body, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (error) {
dispatch({
type: LOGIN_FAIL,
});
}
};
reducers/auth.js
/* eslint-disable default-case */
/* eslint-disable import/no-anonymous-default-export */
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
} from "../actions/types";
const initalState = {
token: localStorage.getItem("token"),
isAuthenticated: false,
loading: true,
user: null,
};
export default function (state = initalState, action) {
const { type, payload } = action;
switch (type) {
case USER_LOADED:
return {
...state,
user: payload,
isAuthenticated: true,
loading: false,
};
case REGISTER_SUCCESS:
case LOGIN_SUCCESS:
localStorage.setItem("token", payload.token);
return {
...state,
...payload,
isAuthenticated: true,
loading: false,
};
case REGISTER_FAIL:
case AUTH_ERROR:
case LOGIN_FAIL:
localStorage.removeItem("token");
return {
...state,
token: null,
isAuthenticated: false,
loading: true,
};
default:
return state;
}
}
store.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const intialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
intialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
I might be wrong, but in you Register.js, in mapStateToProps, you doing following thing:
const { isAuthenticated } = state.auth.isAuthenticated;
const { auth } = state.auth;
It seems like it's not correct, unless your state has following structure:
{
auth: {
isAuthenticated: { isAuthenticated: *value here* },
auth: *value here*
}
}
I think, here might be mistake, and you should destructure values following way:
const { isAuthenticated } = state.auth;
const { auth } = state;
Could someone please let me know why the state isn't being updated from the reducer? The useEffect(()=>{}) isn't being triggered when the state is being returned from the reducer. I have validated the correct information is being passed to the return, but nothing can be seen from the LoginScreen.
Context Script
import React, { createContext, useReducer } from "react";
import userReducer from "./UserReducer";
export const UserContext = createContext();
const initialState = {
userData: [],
isLoggedIn: false,
isAdmin: false,
isEmployee: false,
errorMessage: [{ success: false, statusCode: 0, error: null }],
};
const UserContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(userReducer, initialState);
const registerUser = (user) =>
dispatch({ type: "REGISTER_USER", payload: user });
const loginUser = (user) => dispatch({ type: "LOGIN_USER", payload: user });
const deleteUser = (user) => dispatch({ type: "DELETE_USER", payload: user });
const updateUser = (user) => dispatch({ type: "UPDATE_USER", payload: user });
const contextValues = {
...state,
registerUser,
loginUser,
deleteUser,
updateUser,
};
return (
<UserContext.Provider value={contextValues}>
{children}
</UserContext.Provider>
);
};
export default UserContextProvider;
Reducer Script
import axios from "axios";
axios.defaults.withCredentials = true;
const userReducer = (state = {}, action) => {
let config = {
header: {
"Content-Type": "application/json",
},
};
switch (action.type) {
case "REGISTER_USER":
break;
case "LOGIN_USER":
console.log(state);
const email = action.payload.email;
const password = action.payload.password;
axios
.post("/api/user/login", { email, password }, config)
.then((response) => {
if (response.data.success) {
// localStorage.setItem("authToken", response.data.authToken);
state.userData = response.data.user;
state.isLoggedIn = true;
if (response.data.user.role === 9) {
state.isAdmin = true;
state.isEmployee = true;
} else {
state.isAdmin = false;
state.isEmployee = false;
}
}
})
.catch((error) => {
state.errorMessage = {
success: error.response.data.success,
statusCode: error.response.status,
message: error.response.data.error,
};
});
return {
...state,
userData: [state.userData],
isLoggedIn: state.isLoggedIn,
isAdmin: state.isAdmin,
isEmployee: state.isEmployee,
errorMessage: [state.errorMessage],
};
default:
return state;
}
};
export default userReducer;
Login Form
import { useState, useEffect, useContext } from "react";
import { Link } from "react-router-dom";
import {
Button,
Form,
Grid,
Message,
Segment,
Image,
Container,
} from "semantic-ui-react";
//Custom Imports
import "./LoginScreen.css";
import Logo from "../../../img/logo.png";
//Context
import { UserContext } from "../../context/UserContext";
const LoginScreen = ({ history }) => {
const { userData, loginUser, isLoggedIn, errorMessage, clearErrorMessage } =
useContext(UserContext);
const [user, setUser] = useState({ email: "", password: "" });
const [error, setError] = useState("");
useEffect(() => {
console.log(errorMessage);
if (localStorage.getItem("authToken")) {
history.push("/dashboard");
}
}, [history]);
useEffect(() => {
if (isLoggedIn) {
console.log(userData);
console.log("User is Logged in");
// history.push("/");
}
if (!errorMessage.success && errorMessage.error != null) {
console.log(errorMessage);
setError(errorMessage.message);
setTimeout(() => {
setError("");
}, 5000);
}
}, [userData, errorMessage, isLoggedIn]);
return (
<Container className="login-container">
<Grid
textAlign="center"
style={{ height: "100vh" }}
verticalAlign="middle"
>
<Grid.Column style={{ maxWidth: 450 }}>
<Image src={Logo} className="login-logo" />
<Form size="large" onSubmit={() => loginUser(user)}>
<Segment stacked>
<Form.Input
fluid
icon="user"
iconPosition="left"
placeholder="Email Address"
value={user.email}
onChange={(e) => setUser({ ...user, email: e.target.value })}
/>
<Form.Input
fluid
icon="lock"
iconPosition="left"
placeholder="Password"
value={user.password}
type="password"
onChange={(e) => setUser({ ...user, password: e.target.value })}
/>
{error && <span>{error}</span>}
<Button color="blue" fluid size="large" type="submit">
Login
</Button>
</Segment>
</Form>
<Message>
Don't have an account? <Link to="/register">Sign Up</Link>
</Message>
</Grid.Column>
</Grid>
</Container>
);
};
export default LoginScreen;
Refactor your login function like this
const loginUser({ email, password }) => {
let config = {
header: {
"Content-Type": "application/json",
},
};
axios
.post("/api/user/login", { email, password }, config)
.then((response) => {
if (response.data.success) {
dispatch({ type: 'LOGIN_SUCCESS', payload: response.data });
}
})
.catch((error) => {
dispatch({ type: 'LOGIN_FAILED', payload: error });
});
}
and then your reducer
...
switch(action.type) {
...
case 'LOGIN_SUCCESS':
// return here a new object
// do not mutate the state (state.something = something) is not allowed
...
case 'LOGIN_FAILED':
// handle error
}
Prerequisite Reducer Concepts
Redux and useReducer use reducer like (previousState, action) => newState.
The reducer should be a 'pure' function as in this document. The promises, api calls should not be use inside reducers.
The problem:
Because you call api/promise inside the reducer. The reducer function returns the value before the promise finish. So when the promise finishes, nothing happen.
// A will be return before B, C are going to call
case "LOGIN_USER":
promiseFn()
.then(/* B */ ...)
.catch(/* C */ ...)
// A
return {
...
}
Solution:
Separate the non-pure calls from the reducer. And put them in the other code blocks (like inside hooks, event handlers...).
I'm not sure if the problem is in useSelector or in useDispatch hooks or in another place, so here is the scenario:
Two screens (HomeScreen & AddBlogScreen)
In HomeScreen I click add blog button then it redirect to AddBlogScreen
I input the data, then submit. After the submit is success then redirect to HomeScreen
As mentioned in below pic, I got the no 4 result & I have to refresh to get the no 3 result. But my expectation is no 3 pic without getting the error.
Here is my code:
HomeScreen
import jwtDecode from "jwt-decode";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useHistory } from "react-router";
import { blogList } from "../redux/action";
export const MainScreen = () => {
// const [token, setToken] = useState(localStorage.getItem("token"));
const user = jwtDecode(localStorage.getItem("token"));
const history = useHistory();
const dispatch = useDispatch();
useEffect(() => {
dispatch(blogList());
}, [dispatch]);
const { blog } = useSelector((state) => state.blog);
console.log(blog);
return (
<>
<button
onClick={() => {
localStorage.removeItem("token");
history.push("/");
}}
>
singout
</button>
<button
onClick={() => {
history.push({ pathname: "/Blog", state: user });
}}
>
add blog
</button>
<h1 style={{ color: "red" }}>username: {user.username}</h1>
{blog.map(({ id, b_title, b_content, category_id }) => (
<div key={id}>
<h1
onClick={() =>
history.push({
pathname: "/Edit",
state: { id, b_title, b_content, category_id },
})
}
>
Title: {b_title}
</h1>
<p>Content: {b_content}</p>
</div>
))}
</>
);
};
AddBlogScreen
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { useHistory, useLocation } from "react-router";
import { addBlog } from "../redux/action";
export const AddBlogScreen = () => {
const history = useHistory();
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [category, setCategory] = useState("");
const dispatch = useDispatch();
const location = useLocation();
const author = location.state.id;
const submitHandler = (e) => {
e.preventDefault();
dispatch(addBlog(title, content, author, category));
setTitle("");
setContent("");
setCategory("");
history.push("/Home");
};
return (
<div>
<h1>add blog page</h1>
<form onSubmit={submitHandler}>
<input
type="text"
placeholder="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<br />
<br />
<input
type="text"
placeholder="content"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<br />
<br />
<input
type="text"
placeholder="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
/>
<br />
<br />
<input
type="submit"
value="submit"
disabled={
title === "" || content === "" || category === "" ? true : false
}
/>
</form>
</div>
);
};
actions
import axios from "axios";
import {
LIST_BLOG,
ADD_BLOG,
EDIT_BLOG,
DELETE_BLOG,
LOGIN_USER,
REGISTER_USER,
LOGOUT_USER,
} from "./constant";
// ==================== blog actions ======================
export const blogList = () => async (dispatch) => {
try {
const result = await axios
.get("http://localhost:3001/api/v1/blog?page=0")
.then((res) => res.data.data)
.catch((err) => err);
dispatch({
type: LIST_BLOG,
payload: result,
});
} catch (err) {
dispatch({
payload: err,
});
}
};
export const addBlog =
(title, content, author, category) => async (dispatch) => {
try {
const result = await axios
.post("http://localhost:3001/api/v1/blog", {
blog_title: title,
blog_content: content,
author_id: author,
category_id: category,
})
.then(alert("success add blog"))
.catch((err) => alert(err));
dispatch({
type: ADD_BLOG,
payload: result,
});
} catch (err) {
dispatch({
payload: err,
});
}
};
reducer
const initial_state = {
blog: [],
};
export const blogReducer = (state = initial_state, action) => {
switch (action.type) {
case LIST_BLOG:
return {
...state,
blog: action.payload,
};
case ADD_BLOG:
return {
...state,
blog: action.payload,
};
case EDIT_BLOG:
return {
...state,
blog: action.payload,
};
case DELETE_BLOG:
return {
...state,
blog: action.payload,
};
default:
return state;
}
};
store
import { blogReducer, userReducer } from "./reducer";
import { combineReducers, createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
const reducer = combineReducers({
blog: blogReducer,
user: userReducer,
});
const middleWare = composeWithDevTools(applyMiddleware(thunk));
export const store = createStore(reducer, middleWare);
First of all, the origin of error:
the error says a property named map on blog is not a function, meaning blog is not an array.
This is where it is coming from:
const { blog } = useSelector((state) => state.blog);
Your state is a an ojbect with a property named blog, you can access it these two ways:
const { blog } = useSelector((state) => state);
or
const blog = useSelector((state) => state.blog);
Other issues I noticed :
in addBlog:
1. When you are using try-catch with await, it's not a good idea to use then-catch too.
2.result won't be the blog data you expect. It will be an object, which is an instance of AxiosResponse, which includes the data.
you can extract the data from response object this way:
let response = await axios.post(... // some api request
let {data}=response
I would edit it like this:
export const addBlog =
(title, content, author, category) => async (dispatch) => {
try {
const {data} = await axios
.post("http://localhost:3001/api/v1/blog", {
blog_title: title,
blog_content: content,
author_id: author,
category_id: category,
})
alert("success add blog")
dispatch({
type: ADD_BLOG,
payload: data,
});
} catch (err) {
dispatch({
payload: err,
});
}
};
I found the solution, so in my action I changed it into:
dispatch({
type: LIST_BLOG,
payload: result.data.data,
});
I am making a React app where I need to add Redux using Hooks. Currently, I am stuck in making a POST request and can't figure out after going through the internet, how to make it work. I am on my way to understand how the Redux works and I will be happy for any help on this to make it work, so I can understand what is missing and how to send the data. My components:
App.js:
import { useState } from "react";
import { connect } from 'react-redux';
import "./App.css";
import Posts from "./components/posts";
import { addPost } from "./store/actions/postAction";
function App() {
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
const post = {
title: title,
body: body,
}
addPost(post);
setTitle('');
setBody('');
alert("Post added!");
};
return (
<div className="App">
<Posts />
<form onSubmit={handleSubmit}>
<label>
Mew post:
<input
type="text"
name="title"
placeholder="Add title"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<input
type="text"
name="body"
placeholder="Add body"
value={body}
onChange={e => setBody(e.target.value)}
/>
</label>
<button type="submit">Add</button>
</form>
</div>
);
}
export default connect()(App);
postAction.js
import axios from "axios";
import { GET_POSTS, ADD_POST, POSTS_ERROR } from "../types";
const url = "http://localhost:8002/";
export const getPosts = () => async (dispatch) => {
try {
const response = await axios.get(`${url}posts`);
dispatch({
type: GET_POSTS,
payload: response.data,
});
} catch (error) {
dispatch({
type: POSTS_ERROR,
payload: error,
});
}
};
export const addPost = (post) => (dispatch) => {
try {
const response = axios.post(`${url}`, {post});
dispatch({
type: ADD_POST,
payload: response.data,
});
} catch (error) {
dispatch({
type: POSTS_ERROR,
payload: error,
});
}
};
postReducer.js
import { ADD_POST, GET_POSTS, POSTS_ERROR } from "../types";
const initialState = {
posts: []
};
const postReducer = (state = initialState, action) => {
switch (action.type) {
case GET_POSTS:
return {
...state,
posts: action.payload
};
case ADD_POST:
return {
...state,
posts: action.payload
};
case POSTS_ERROR:
return {
error: action.payload
};
default:
return state;
}
};
export default postReducer;
posts.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getPosts } from "../store/actions/postAction";
const Posts = () => {
const dispatch = useDispatch();
const postsList = useSelector((state) => state.postsList);
const { loading, error, posts } = postsList;
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
return (
<>
{loading
? "Loading..."
: error
? error.message
: posts.map((post) => (
<div className="post" key={post.id}>
<h4>{post.title}</h4>
<p>{post.body}</p>
</div>
))}
</>
);
};
export default Posts;
App.js -> change to export default connect(null, {addPost})(App);
I create a app, everything works fine, but when i move route or reload the page, the state back to the default situation, maybe i able to fix this problem using library as react-persist or found some way to put the state on the local Storage, i don't think this approach is the best way.Maybe you another alternative?
`store.js`
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducer/rootReducer";
const initialState = {};
export default createStore(rootReducer, initialState, applyMiddleware(thunk));
`reducer`
import {
REGISTER_USER,
REGISTER_FAIL,
LOGIN_USER,
LOGIN_FAIL,
// LOAD_USER,
// LOAD_USER_FAIL,
// LOGOUT_USER,
} from "../constants";
const initialState = {
user: null,
loading: true,
isAuthenticated: false,
error: null,
};
export default (state = initialState, action) => {
switch (action.type) {
case REGISTER_USER:
return {
...state,
user: action.payload,
loading: false,
isAuthenticated: true,
error: null,
};
case REGISTER_FAIL:
return {
...state,
user: null,
loading: true,
isAuthenticated: false,
error: action.payload,
};
case LOGIN_USER:
localStorage.setItem("token", action.payload.token);
return {
...state,
user: action.payload,
loading: false,
isAuthenticated: true,
error: null,
};
case LOGIN_FAIL:
return {
...state,
user: null,
loading: true,
isAuthenticated: false,
error: action.payload,
};
default:
return state;
}
};
`Login`
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Redirect, useHistory } from "react-router-dom";
import { loginAction } from "../actions/loginAction";
const Login = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.auth);
let history = useHistory();
const [msg, setMsg] = useState("");
const [state, setState] = useState({
email: "",
password: "",
});
useEffect(() => {
let interval;
if (data.error) {
setMsg(data.error.msg);
interval = setTimeout(() => {
setMsg("");
}, 1000);
}
if (data.user) {
setMsg(data.user.msg);
interval = setTimeout(() => {
setMsg("");
}, 1000);
}
return () => {
clearTimeout(interval);
};
}, [data]);
const handleSubmit = (e) => {
e.preventDefault();
dispatch(loginAction(state));
};
const handleChange = (e) => {
setState({ ...state, [e.target.name]: e.target.value });
};
if (data.isAuthenticated) {
history.push("/dashboard");
}
console.log(data);
return (
<>
<span>{msg}</span>
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<input name="email" onChange={handleChange} value={state.email} />
<input name="password" onChange={handleChange} value={state.password} />
<input type="submit" value="Login" />
</form>
</>
);
};
export default Login;
register
import React, { useState, useEffect } from "react";
import { registerAction } from "../actions/registerAction";
import { useDispatch, useSelector } from "react-redux";
import { Redirect } from "react-router-dom";
const Register = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.auth);
const [msg, setMsg] = useState("");
const [state, setState] = useState({
name: "",
email: "",
password: "",
photo: "",
});
useEffect(() => {
let interval;
if (data.error) {
setMsg(data.error);
interval = setTimeout(() => {
setMsg("");
}, 1000);
}
if (data.user) {
setMsg(data.user.msg);
interval = setTimeout(() => {
setMsg("");
}, 1000);
}
return () => {
clearTimeout(interval);
};
}, [data]);
if (data.user) {
return <Redirect to="/login" />;
}
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("name", state.name);
formData.append("email", state.email);
formData.append("password", state.password);
formData.append("photo", state.photo);
dispatch(registerAction(formData));
};
const handleChange = (e) => {
setState({ ...state, [e.target.name]: e.target.value });
};
const handleOnUploadFile = (e) => {
setState({ ...state, photo: e.target.files[0] });
};
return (
<>
<span>{msg}</span>
<form onSubmit={handleSubmit}>
<label>Name</label>
<input name="name" onChange={handleChange} value={state.name} />
<label>Email</label>
<input name="email" onChange={handleChange} value={state.email} />
<label>Password</label>
<input name="password" onChange={handleChange} value={state.password} />
<input
name="photo"
type="file"
accept="image/*"
onChange={handleOnUploadFile}
/>
<input type="submit" />
</form>
</>
);
};
export default Register;
You should store a token in Local storage and pull it out and verify it upon reload. Also you should make a new action to load user from that token. States will alwayr reset to initial state on reload.
I cant see ur registerAction page but the loadUser should be something like this
// Load User
export const loadUser = () => async (dispatch) => {
try {
const res = await axios.get('/api/auth');
dispatch({
type: USER_LOADED,
payload: res.data,
} catch (err) {
dispatch({
type: AUTH_ERROR,
payload: err.response.data,
});
}
};
/api/auth is like this
// #route GET api/auth
// #desc Get logged in user (Login)
// #access Private
router.get('/', auth, async (req, res) => {
try {
const user = await Planner.findById(req.user.id).select('-password');
if (user.type === 'Admin') {
res.json({ user, admin: true });
} else {
res.json({ user, admin: false });
}
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
You need to use jwttoken for example as an auth middle ware
the middleware file would be like this
const jwt = require('jsonwebtoken');
const config = require('config');
module.exports = function (req, res, next) {
//Get token from header
const token = req.header('x-auth-token');
// Check if not token
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
try {
const decoded = jwt.verify(token, config.get('jwtSecret'));
req.user = decoded.user;
next();
} catch (err) {
res.status(401).json({ msg: 'Token is not valid' });
}
};
I have implemented a similar redux of the contact keeper project by Brad Travery
You can see the full code here am sure it will help
https://github.com/madaher-dev/contact-keeper
Enjoy