My useEffect code looks like this:
useEffect(() => {
if (user) {
setBalance(user.user_balance);
console.log("user.user_balance");
console.log(user.user_balance);
}
}, [user]);
Full code : https://pastebin.com/CP6sLNQZ
The useEffect is supposed to get triggered every time user (user_balance) gets updated in the database but it is not getting triggered. User is a props and it is passed from redux to the component.
The action for updating the balance is here:
import axios from "axios";
import { returnErrors } from "./errorActions";
import { UPDATE_BALANCE_SUCCESS } from "./types";
//update balance
export const updateBalance = ({ email, user_balance }) => (dispatch) => {
// Headers
const config = {
headers: {
"Content-Type": "application/json",
},
};
// Request body
const body = JSON.stringify({ email, user_balance });
axios
.post("/api/users/updatebalance", body, config)
.then((res) =>
dispatch({
type: UPDATE_BALANCE_SUCCESS,
payload: { email, user_balance },
})
)
.catch((err) =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
The reducer:
import { UPDATE_BALANCE_SUCCESS } from "../actions/types";
const initalState = {
email: null,
user_balance: null,
};
export default function (state = initialState, action) {
switch (action.type) {
case UPDATE_BALANCE_SUCCESS:
return {
...state,
email: action.payload.email,
user_balance: action.payload.user_balance,
};
}
}
And the end-point:
// #route POST api/users/updatebalance
// #description Update user balance
// #access Public
router.post("/updatebalance", (req, res) => {
const { email, user_balance } = req.body;
User.findOneAndUpdate(
{ email },
{ $inc: { user_balance: user_balance } },
function (err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
}
);
});
Also where auth comes from:
This is the auth reducer: https://pastebin.com/upHdvtiF
These are the actions: https://pastebin.com/Ajp7S1Yi
And this is the end point: https://pastebin.com/NwCHCitY
I got a log in and register page and once a user gets registered auth gets created. In the actions link you can see the login and register action.
I think you have to add the user layer
const initalState = {
user: {
email: null,
user_balance: null,
}
};
and the reducer
export default function (state = initialState, action) {
switch (action.type) {
case UPDATE_BALANCE_SUCCESS:
return {
...state,
user: {
...state.user,
email: action.payload.email,
user_balance: action.payload.user_balance,
}
};
}
}
Related
i am trying to send the error messages that sent from my server ( express ) to axios and the error message displays in toastify component but the error message doesn't show up here is the login axios function with the toastify how can i display toastify message inside my page from redux ?
here is my code :
// redux controller
const login = async (username, password) => {
await axios.post("/login",{username,password,},
{ withCredentials: true });};
// reducer page
export function generateError(prop) {
return function (dispatch) {
dispatch({
type: "USER_FAIL"
});
toast.error(prop);
};
}
export function generateSuccess(prop) {
return function (dispatch) {
dispatch({
type: "USER_SUCCESS"
});
toast.success(prop);
};
}
export const login = createAsyncThunk(
"/login",
async ({ username, password }) => {
try {
const data = await authService.login(username, password);
if (data) {
if (data.errors) {
const { username, password } = data.errors;
if (username) generateError(username)
else if (password) generateError(password);
} else {
generateSuccess(data.success);
}
}
return { user: data };
} catch (error) {
console.log(error);
}
}
);
// login page
const handleSubmit = (e) => {
e.preventDefault();
dispatch(login({ username, password }));
}
i am using react-tostify and #redux-toolkit but the message doesn't display inside my page
i fixed it and here is my code :
// auth.js ( redux page )
export const login = createAsyncThunk(
"/login",
async ({ username, password }) => {
try {
const {data} = await axios.post(
"/login",
{
username,
password,
},
{ withCredentials: true }
);
return { user: data };
} catch (error) {
console.log(error);
}
});
const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };
const authSlice = createSlice({
name: "auth",
initialState,
extraReducers: {
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
}})
const { reducer } = authSlice; export default reducer;
Login Page :
const { isLoggedIn } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
dispatch(login({ username, password })).then(data => {
console.log(data)
if (data.payload.user) {
if (data.payload.user.errors) {
const { username, password } = data.payload.user.errors;
if (username) generateError(username)
else if (password) generateError(password);
} else {
generateSuccess(data.success);
navigate("/dashboard");
}
}
})
}
i realized when i back the data it has an object name payload i used it to get the error messages from express and then i put the message in toastify function gettingError and here it is
const generateError = error => {
toast.error(error, {
position: "bottom-right",
})
}
Hai I'm also looking for the same problem while searching I found a solution at with this : react-toastify-with-redux
my Code : authAction.js
import 'react-toastify/dist/ReactToastify.min.css';
import { toast} from 'react-toastify';
export const registerUser = (userData) => dispatch =>{
axios.post('user/register',userData)
.then(res=>toast.success('Your Account Created Successfully ๐'))
.then(res=> window.location = '/authentication/sign-in')
.catch(err=>dispatch(
{
type: GET_ERRORS,
payload: err.response.data
}
),toast.error("Error ๐ฃ"))
// .catch((err)=> {return })
};
On your signUp page just add
<ToastContainer />
That's all ...
This answer is probably late. But I came across this problem and decided to do it my way. I know there is toast. promise to handle promises and I don't want to call dispatch.then every time. So I can up with passing dispatch to my action wrapper. Here is my code.
// utils.ts
type ArgumentTypes<F extends CallableFunction> = F extends (
...args: infer A
) => any
? A[0]
: never;
export const withToast = <T = AnyAction | typeof createAsyncThunk>(
action: T,
{ pending, error, success }: ToastPromiseParams<T>
) => {
return (
dispatch: ReturnType<typeof useAppDispatch>,
actionParams?: ArgumentTypes<T & CallableFunction> | void
) => {
const promise = dispatch(
(action as CallableFunction)(actionParams as any)
).unwrap();
toast.promise(promise, {
pending,
error,
success,
});
};
};
// actions.ts
export const login = createAsyncThunk(
"user/login",
async (payload: {
email: string;
password: string;
}): Promise<Partial<LoginAPIResponse>> => {
const { data } = await axios.post(`${API}/${LOGIN_EP}/`, payload);
return data;
}
);
export const loginWithToast = withToast(login, {
pending: "Logging in...",
error: {
render: (error: any) => {
return error?.password || error?.email
? "Invalid email or password"
: "Something went wrong";
},
},
success: "Logged in successfully",
});
// usage in component
const dispatch = useAppDispatch();
loginWithToast(dispatch, {
email: values.email.value,
password: values.password.value,
});
First createAsyncThunk:
import { coreAxios } from "utilities/axios"; // Own customized axios
import { createAsyncThunk } from "#reduxjs/toolkit";
const BASE_URL = process.env.REACT_APP_MAIN_URL
export const GetProducts = createAsyncThunk(
"inventory/GetProducts",
async () => {
const {data} = await coreAxios.get(`${BASE_URL}/api/product/list/`);
return data
}
);
Second createSlice:
import { createSlice } from "#reduxjs/toolkit";
import { GetProducts } from "services/inventory/product.service";
import { toast } from 'react-toastify';
export const productSlice = createSlice({
name: "products",
initialState: {
productsList: [],
productsLoading: false,
productsError: null,
},
extraReducers:
(builder) => {
builder.addCase(GetProducts.pending, (state) => {
toast.loading('Promise is pending...')
state.productsLoading = true
});
builder.addCase(GetProducts.fulfilled, (state, action) => {
toast.dismiss();
toast.success('Promise resolved ๐');
state.productsList = action.payload
state.productsLoading = false
state.productsError = null
});
builder.addCase(GetProducts.rejected, (state, action) => {
toast.dismiss();
toast.error('Promise rejected ๐คฏ ๐ฃ')
state.productsLoading = false
state.productsError = action.error?.message
});
},
});
export default productSlice.reducer;
Third page:
import { ToastContainer } from 'react-toastify';
import { useSelector, useDispatch } from "react-redux";
import { GetProducts } from 'services/inventory/product.service';
const Product = () => {
const { productsList, productsLoading, productsError } = useSelector((state) => state.products);
const dispatch = useDispatch();
useEffect(() => {
dispatch(GetProducts());
}, []);
return (
<div className="grid crud-demo">
<h1>Hello Alim</h1>
<ToastContainer />
</div>
);
}
I have created an authentification system in react with redux and axios but I can`t figure out how to render the data in my components.
This is my actions/auth.js:
import axios from 'axios';
import {
SIGNUP_SUCCESS,
SIGNUP_FAIL,
LOGIN_SUCCESS,
LOGIN_FAIL,
ACTIVATION_SUCCESS,
ACTIVATION_FAIL,
RESET_PASSWORD_SUCCESS,
RESET_PASSWORD_FAIL,
RESET_PASSWORD_CONFIRM_SUCCESS,
RESET_PASSWORD_CONFIRM_FAIL,
LOGOUT,
USER_LOADED_SUCCESS,
USER_LOADED_FAIL,
AUTHENTICATED_FAIL,
AUTHENTICATED_SUCCESS
} from './types';
export const checkAuthenticated = () => async dispatch => {
if (typeof window == 'undefined') {
dispatch({
type: AUTHENTICATED_FAIL
});
}
if (localStorage.getItem('access')) {
const config = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ token: localStorage.getItem('access') });
try {
const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/verify/`, body, config);
if (res.data.code !== 'token_not_valid') {
dispatch({
type: AUTHENTICATED_SUCCESS
});
} else {
dispatch({
type: AUTHENTICATED_FAIL
});
}
} catch (err) {
dispatch({
type: AUTHENTICATED_FAIL
});
}
} else {
dispatch({
type: AUTHENTICATED_FAIL
});
}
};
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 res = await axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config);
dispatch({
type: USER_LOADED_SUCCESS,
payload: res.data
});
} catch (err) {
dispatch({
type: USER_LOADED_FAIL
});
}
} else {
dispatch({
type: USER_LOADED_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(`${process.env.REACT_APP_API_URL}/auth/jwt/create/`, body, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
});
dispatch(load_user());
} catch (err) {
dispatch({
type: LOGIN_FAIL
});
}
};
export const logout = () => dispatch => {
dispatch({ type: LOGOUT });
};
This is my reducers/auth.js:
import {
SIGNUP_SUCCESS,
SIGNUP_FAIL,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
AUTHENTICATED_FAIL,
AUTHENTICATED_SUCCESS,
USER_LOADED_SUCCESS,
USER_LOADED_FAIL
} from '../actions/types';
const initialState = {
access: localStorage.getItem('access'),
refresh: localStorage.getItem('refresh'),
isAuthenticated: null,
user: null
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch(type) {
case AUTHENTICATED_SUCCESS:
return {
...state,
isAuthenticated: true
}
case LOGIN_SUCCESS:
localStorage.setItem('access', payload.access);
return {
...state,
isAuthenticated: true,
access: payload.access,
refresh: payload.refresh
}
case USER_LOADED_SUCCESS:
return {
...state,
user: payload
}
case AUTHENTICATED_FAIL:
return {
...state,
isAuthenticated: false
}
case USER_LOADED_FAIL:
return {
...state,
user: null
}
case LOGIN_FAIL:
case LOGOUT:
localStorage.removeItem('access');
localStorage.removeItem('refresh');
return{
...state,
access: null,
refresh: null,
isAuthenticated: false,
user: null
}
default:
return state
}
}
If I log in and use the redux Devtool I can see this state:
{
auth: {
access: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjAzNDgzODY1LCJqdGkiOiJhYTAzYzIzNTUwN2M0YTkxYjA2NjNmNDc0ZTU2MjIxMSIsInVzZXJfaWQiOjF9.Jyld4U7i6EqmsNoi0_qT9O9Kcu1TiEuyLLYCWWaoBrU',
refresh: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwMzU2OTk2NSwianRpIjoiOWIzMWIyN2M1ODkyNDRiZDk3Y2EwMDI1NTY2Mzk3ZWMiLCJ1c2VyX2lkIjoxfQ.UgH_753OoWD3NXiwPwa1645_vIHUl-FwyvQMJWMgHtk',
isAuthenticated: true,
user: {
name: 'Jonas Levin',
id: 1,
email: 'jonaslevin1903#gmail.com'
}
}
}
But I can`t figure out how to display the data, for example user.name.
I already tried to use mapStateToProps in one of my components but I get the error: "TypeError: Cannot read property 'name' of undefined"
const mapStateToProps = state => ({
userName: state.user.name,
userEmail: state.user.email
});
Edit
This is the response data that I get. But as you can see there is another API call which is still from the login page where I was on before I got redirected to '/' and that light red /me call has an error message in it because when your on the login page you don`t have an access token.
How can I access this response data in my Components to render the Name?
Store.js:
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
I managed to access the username in my layout.js file by adding the state to the props:
const mapStateToProps = (state, ownProps) => {
return {
isAuthenticated: state.auth.isAuthenticated,
user: state.auth.user,
props: ownProps
}
};
I used ownProps to be able to also use props.children in the layout container. Than I gave tham as parameters to the layout container and was able to access the username with user.name.
Iยดm not entirely sure why it worked now and not before when I already tried to use mapStateToProps.
this is how you should do it access auth reducer then th user
const mapStateToProps = state => ({
userName: state.auth.user.name,
userEmail: state.auth.user.email
});
this is how redux works , let's say this is your store
import {cartReducer} from './reducers/CartReducer'
import { authReducer } from './reducers/AuthReducer'
import { ordersReducer } from './reducers/OrdersReducer'
import { errorsReducer } from './reducers/ErrorsReducer'
const initialState={
products:{
items:[],
filterdProducts:[]
},
cart:{
items:[],
},
orders:{
items:[],
canOrder:true,
},
auth: {
access: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjAzNDgzODY1LCJqdGkiOiJhYTAzYzIzNTUwN2M0YTkxYjA2NjNmNDc0ZTU2MjIxMSIsInVzZXJfaWQiOjF9.Jyld4U7i6EqmsNoi0_qT9O9Kcu1TiEuyLLYCWWaoBrU',
refresh: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwMzU2OTk2NSwianRpIjoiOWIzMWIyN2M1ODkyNDRiZDk3Y2EwMDI1NTY2Mzk3ZWMiLCJ1c2VyX2lkIjoxfQ.UgH_753OoWD3NXiwPwa1645_vIHUl-FwyvQMJWMgHtk',
isAuthenticated: true,
user: {
name: 'Jonas Levin',
id: 1,
email: 'jonaslevin1903#gmail.com'
}
},
error:{
msg:null,
status:null,
id:null
}
}
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(combineReducers({
products: productsReducer,
cart : cartReducer ,
orders : ordersReducer ,
auth : authReducer,
error : errorsReducer ,
}),
initialState,
composeEnhancer(applyMiddleware(thunk))
)
export default store
if you want to access user from any other component you'mm need to access auth reducer first , same for items you can either access products.items or cart .items and so on
If you use a functional component, you could use useSelector hook.
const user = useSelector(state => state.auth.user)
i am working on createContext and reducers, with reducers i am updating state value, but it is not updating value at a time, i did console.log(user); in onSubmitSave(), but in console log i am not getting its updated state at a time, i can see it works when i clicked on signin again, here i have attached my whole code, can anyone please look into it, and help me to resolve this issue ?
auth.js
import { createContext } from 'react';
const AuthContext = createContext({
user: null,
hasLoginError: false,
login: () => null,
logout: () => null
});
export default AuthContext;
signin.js
// Login button Click
async function onSubmitSave(e) {
e.preventDefault();
const messages = {
'itemUsername.required': 'Username cannot be empty.',
'itemPassword.required': 'Password cannot be empty.',
};
const rules = {
itemUsername: 'required',
itemPassword: 'required|string',
};
validateAll(state, rules, messages)
.then(async () => {
let data = await login(itemUsername, itemPassword);
console.log(data);
console.log("sdsds");
console.log(user);
setUserData(user);
})
.catch(errors => {
const formattedErrors = {
};
errors.forEach(error => formattedErrors[error.field] = error.message);
setState({
...state,
errors: formattedErrors
});
})
}
// End Login
App.js
const reducer = (state, action) => {
switch (action.type) {
case "login": {
const { username, password } = action.payload;
return {
...state,
hasLoginError: false,
user: {
id: 1,
username: USERNAME,
firstName: "Dev",
lastName: "To"
}
};
}
case "logout":
return {
...state,
user: null
};
default:
throw new Error(`Invalid action type: ${action.type}`);
}
};
I'm having an issue trying to use Redux Thunk for implementing JWT Authentication in my app: when I perform login and retrieve the promise from the action, I do not get the updated store (I still have the previous value).
Here is my code:
const Login = ({ doLogin, token }) => {
const submitForm = (e) => {
doLogin(email, password).then(function () {
console.log(token);
});
}
};
return (
// Some JSX that calls submitForm()
);
};
Login.propTypes = {
token: PropTypes.string.isRequired,
doLogin: PropTypes.func.isRequired,
};
function mapStateToProps(state) {
console.log(state);
return {
token: state.auth.access_token,
};
}
const mapDispatchToProps = { doLogin };
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Here is my action creator:
export function doLogin(email, password) {
return (dispatch) => {
dispatch(start());
return axios
.post(config.api.url + "/login", {
email,
password,
})
.then((success_rsp) => {
if (success_rsp.data.success) {
dispatch(success(success_rsp.data.access_token));
} else {
dispatch(error());
}
})
.catch((error_rsp) => dispatch(error()));
};
function start() {
return { type: types.AUTH_LOGIN };
}
function success(access_token) {
return {
type: types.AUTH_LOGIN_SUCCESS,
is_authenticated: true,
access_token: access_token
};
}
function error() {
return {
type: types.AUTH_LOGIN_ERROR,
};
}
}
And here is my reducer:
const authReducer = (state = initialState.auth, action) => {
switch (action.type) {
case types.AUTH_LOGIN:
return {
...state,
is_loading: true,
};
case types.AUTH_LOGIN_SUCCESS:
return {
...state,
is_loading: false,
is_authenticated: action.is_authenticated,
access_token: action.access_token
};
case types.AUTH_LOGIN_ERROR:
return {
...state,
is_loading: false,
is_authenticated: false,
access_token: "",
};
default:
return state;
}
};
The problem is that when I get in the Promise of my Login component, it logs the previous value of the token, and not the one received by the API.
However, when I log the state in the mapStateToProps function, I see that the function is called 3 times in total (one when arriving on the page, one when starting the doLogin, and one on the success), and it logs the right value for the token.
Do you know why the token I get is not updated? And how I could get an updated one?
Thank you!
When a user tries to login, the Local state or Redux state should be updated. this.props.checkLogin() refers to the Redux store.
If the login is succesful, the Redux store should be updated.
Because this information is needed throughout the whole application.
If not succesful, the localstate should be updated with an error
message (a boolean which is called 'showError').
In the setup below, the results are always updated in the Redux store (which is not what I want, but I dont see any other solution yet). The main problem is that I can not catch an error in the checkLogin() of the component, if there is an error in the action.
// LOGIN COMPONENT (Code 1)
checkLogin(e){
e.preventDefault()
var username = e.target.elements.username.value
var password = e.target.elements.password.value
this.props.checkLogin(username, password)
}
// Redux action
export const checkLogin = (username, password) => async dispatch => {
axios.post('/api/login', {
username: username,
password: password
})
.then(res => {
const token = res.data.token;
localStorage.setItem('jwtToken', token);
setAuthorizationToken(token);
dispatch(setCurrentUser(jwt.decode(token)))
})
.catch(err => {
dispatch({
type: AUTH_ERROR,
payload: true
})
})
}
However, if there is an error in (Code 1), the Redux state is updated (with the AUTH_ERR action). I want to get rid of this action, as I only want it in local state. Because the boolean of an error login attempt should not be stored in Redux.
The only solution which I could think of is shown below (Code 2).
The POST-request has moved to the component itself (which is not nice in my opinion).
// LOGIN COMPONENT (Code 2)
checkLogin(e){
e.preventDefault()
var username = e.target.elements.username.value
var password = e.target.elements.password.value
axios.post('/api/login', {
username: username,
password: password
})
.then(res => {
this.props.checkLogin(username, password)
}
.catch(err => {
this.setState({
showError: true
})
})
}
// Redux action
export const checkLogin = (username, password) => async dispatch => {
const token = res.data.token;
localStorage.setItem('jwtToken', token);
setAuthorizationToken(token);
dispatch(setCurrentUser(jwt.decode(token)))
}
My main question is: what is the cleanest way of solving this? Is there some sort of guide which I can follow? The code (Code 2) works, but it definitely lacks design principles.
Another way of solving this could be something like this: use the first code and throw an error in the catch of the POST-request. Which is catched by the component, like this:
// LOGIN COMPONENT (Code 3)
checkLogin(e){
e.preventDefault()
var username = e.target.elements.username.value
var password = e.target.elements.password.value
this.props.checkLogin(username, password).catch(res => {
this.setState({
showError: true
})
})
}
// Redux action
export const checkLogin = (username, password) => async dispatch => {
axios.post('/api/login', {
username: username,
password: password
})
.then(res => {
const token = res.data.token;
localStorage.setItem('jwtToken', token);
setAuthorizationToken(token);
dispatch(setCurrentUser(jwt.decode(token)))
})
.catch(err => {
// Return an error here which is cacthed in the component
})
}
In the above code (Code 3), I can't see how to solve this. Is it possible to throw an error in the Redux action, which is then catched by the checkLogin() of the Login-component? Or should I complete take another path in solving this?
You have connected redux to manage the state of the application, why do you want to avoid storing an error in redux? A typical implementation of the react-redux (with redux-thunk middleware) authentication process is as follows.
actions.js
export const auth = (username, password) => {
dispatch(authStart())
axios.post('/api/login', {
username: username,
paswword: password
}).then(request => {
dispatch(authSuccess(response.authToken, response.userId))
}).catch(error => {
dispatch(authFail(error))
})
})
const authStart = () => {
return { type: 'AUTH_START'}
}
const authSuccess = (authToken, userId) => {
return { type: 'AUTH_SUCCESS', authToken: authToken, userId: userId }
}
const authFail = (error) => {
return { type: 'AUTH_FAIL', error: error }
}
reducer.js
const initialState = {
userId: null,
authToken: null,
error: null
loading: false
}
const authStart = (state, action) => {
return { ...state, loading: true, error: null }
}
const authSuccess = (state, action) => {
return {
...state,
loading: false,
authToken: action.authToken,
userId: actions.userId
}
}
const authStart = (state, action) => {
return { ...state, loading: false, error: error }
}
const authReducer = (state = initialState, action) => {
switch action.type:
case 'AUTH_START':
return authStart(state, action)
case 'AUTH_SUCCESS':
return authSuccess(state, action)
case 'AUTH_FAIL':
return authFail(state, action)
default:
return state
}
component.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { auth } from './actions.js'
class MyComponent extends Component {
checkLogin(e){
e.preventDefault()
var username = e.target.elements.username.value
var password = e.target.elements.password.value
this.props.signIn(username, password)
}
render() {
let content = <div>Login succeed. User id is {this.props.userId}</div>
if (this.props.loading) {
content = <div>Loading...</div>
}
if (this.props.error) {
content = <div>Error: {this.props.error}</div>
}
return <React.Fragment>{content}</React.Fragment>
}
}
const mapStateToProps = state => {
return {
error: state.auth.error,
loading: state.auth.loading,
userId: state.auth.userId
}
}
const mapDispatchToProps = dispatch => {
return {
signIn: (username, password) => dispatch(auth(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)