Preface: I'm new to react.
I'm creating a project based on React, Redux.
I want to set a loading status when I press the register button on the register component.
I did a lot of research for a possible solution, but wasn't able to find anything useful for my situation.
What's the best way to fix this?
Register reducer
const initialState = {
pending: false,
users: [],
error: null,
showModal: false,
loading: false
}
export function userReducer(state = initialState, action) {
switch (action.type) {
case 'TOGGLE_LOADING': return {
...state,
loading: !state.loading
}
case 'USER_ADD':
{
debugger;
state.users = state.users.concat(action.payload);
return {
...state,
loading: false,
users: state.users
}
}
case FETCH_USERS_PENDING:
return {
...state,
pending: true,
loading: false
}
case FETCH_USERS_SUCCESS:
return {
...state,
pending: false,
loading: false,
users: action.payload
}
case FETCH_USERS_ERROR:
return {
...state,
pending: false,
loading: false,
error: action.error
}
default:
return state;
}
}
export default userReducer;
Register action
export const userRegisterFetch = user => {
user.Username = user.Mobile;
return dispatch => {
dispatch({ type: 'TOGGLE_LOAD' })
return fetch(`${baseUrl}/users/Register`,
{
method: 'POST', body: JSON.stringify(user), headers: {
'Content-Type': 'application/json',
}
}
)
.then(resp => resp.json())
.then(data => {
if (data.result == undefined) {
return alert('error');
}
if (!data.result) {
alert(data.message);
}
else {
const location = {
pathname: '/Users/Confirm',
state: { mobile: user.Mobile }
}
history.push(location);
}
})
}
}
Register.js component
const { loading } = this.props
return(
{loading ? <Loading /> :
<Form>
...my form
</Form>
}
)
I think you only need 3 reducers to be honest, FETCH_USERS_INIT, FETCH_USERS_SUCCESS and FETCH_USERS_FAIL.
Register reducer
const initialState = {
users: [],
loading: false,
error: null
};
function userReducer(state = initialState, action) {
switch (action.type) {
case 'FETCH_USERS_INIT':
return {
...state,
loading: true
};
case 'FETCH_USERS_SUCCESS':
return {
...state,
loading: false,
error: null,
users: action.payload.users
};
case 'FETCH_USERS_FAIL':
return {
...state,
loading: false,
error: action.payload.error
};
default:
return initialState;
}
}
export default userReducer;
export const userRegisterFetch = user => {
user.Username = user.Mobile;
return async dispatch => {
dispatch({ type: 'FETCH_USERS_INIT' });
fetch(`${baseUrl}/users/Register`, {
method: 'POST',
body: JSON.stringify(user),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
/* dispatch 'FETCH_USERS_SUCCESS' with the list of users */
}).catch(error => {
/* dispatch 'FETCH_USERS_FAIL' with the corresponding error */
});
};
};
The action is not finished but i think it's clear how to finish it. Let me know if you have any doubt.
Related
I am using Redux for state management, I have faced an issue in reducer function
here is the image of my console, You can see the Product Action is providing my data but the reducer is not passing on my function
here is my code of ProductAction:
export const getProductsbyFind = (myvariable) =>async (dispatch)=>{
try {
console.log(myvariable)
dispatch({type: ALL_PRODUCTS_REQUEST_BY_ID})
const{ data } = await axios.get(`/api/v1/product/${myvariable}`)
console.log(data)
dispatch({
type: ALL_PRODUCTS_SUCCESS_BY_ID,
payload: data
})
} catch (error) {
dispatch({
type:ALL_PRODUCTS_FAIL,
payload: error.response.data.message
})
}
}
here is the code of Reducer:
export const productReducersById = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCTS_REQUEST_BY_ID:
return {
loading: true,
products: []
}
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
products: action.payload.products,
productsCount: action.payload.productsCount
}
case UPDATE_QUANTITY_BY_ID:
const { index, quantity } = action.payload;
const prods = state.products.map((p, i) => {
if (i !== index)
return p;
return {
...p,
quantity
}
});
return {
loading: true,
products: prods
}
case ALL_PRODUCTS_FAIL_BY_ID:
return {
loading: false,
error: action.payload
}
case CLEAR_ERRORS_BY_ID:
return {
...state,
error: null
}
default:
return state
}
}
here is the code of my page where I want to get my data:
const { loading, products, error, productCount } = useSelector(state => state.products);
console.log(products)
useEffect(() => {
dispatch(getProductsbyFind(myvariable));
}, [dispatch])
You have a typo in your reducer:
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
- products: action.payload.products,
+ products: action.payload.product,
productsCount: action.payload.productsCount
}
(Also, productsCount does not exist in your payload, so that will become undefined.)
Store changes not immediately visible to component due to this error message not showing in component whenever request get failed. From reducer, state update take some time to return the update value to component. Hence, component always return as empty msg which is default value present in reducer
Api.js
export const createCategory = async (category, authtoken) => {
return await axios.post(
`${process.env.REACT_APP_API}/category/create`,
category,
{
headers: {
authtoken,
},
}
);
};
category.saga.js
export function* createCategoryAsync({ payload: { name, token } }) {
try {
yield delay(1000);
const response = yield call(createCategory, { name }, token);
yield delay(1000);
console.log("===response", response);
if (response.status === 200 && response.status < 300) {
yield put(createCategorySuccess(response.data.name));
}
console.log("===response", response);
} catch (error) {
yield put(createCategoryFail(error.response.data));
}
}
category.reducer.js
import CategoryActionTypes from "./category.types";
const INITIAL_STATE = {
categoryName: "",
categories: [],
error: false,
errorMsg: "",
};
const categoryReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case CategoryActionTypes.LOAD_CATEGORY_START:
return {
...state,
loading: true,
};
case CategoryActionTypes.LOAD_CATEGORY_SUCCESS:
return {
...state,
loading: false,
categories: action.payload,
};
case CategoryActionTypes.SET_CATEGORY_EMPTY:
return {
...state,
categoryName: "",
};
case CategoryActionTypes.CREATE_CATEGORY_START:
return {
...state,
loading: true,
};
case CategoryActionTypes.CREATE_CATEGORY_SUCCESS:
return {
...state,
loading: false,
categoryName: action.payload,
};
case CategoryActionTypes.LOAD_CATEGORY_FAIL:
case CategoryActionTypes.CREATE_CATEGORY_FAIL:
return {
...state,
loading: false,
error: true,
errorMsg: action.payload,
};
default:
return state;
}
};
export default categoryReducer;
Component.js
const Component = () => {
useEffect(() => {
loadCateories();
}, []);
const { categories, loading, categoryName, error, errorMsg } = useSelector(
(state) => ({
...state.category,
})
);
const loadCateories = () => {
dispatch(loadCategoryStart());
};
console.log("==errorMsg", errorMsg);
const {
user: { token },
} = useSelector((state) => ({ ...state }));
const handleSubmit = (e) => {
e.preventDefault();
// setLoading(true);
// dispatch(setCategoryEmpty());
dispatch(createCategoryStart({ name, token }));
if (categoryName) {
toast.success(`${name} is created`);
setName("");
loadCateories();
} else {
toast.error(errorMsg && errorMsg);
setName("");
}
};
}
I wanted to know how I can pass the state into an action so that it can use the state to make an API call. the state I want to pass is the input because it's the image URL that gets sent to Clarifai's servers to predict the celebrity. The handle search is responsible for updating state to the URL.
I've tried to use get state with no luck
This is my action
export const requestPrediction = () => {
return function (dispatch, getState) {
dispatch(fetchCelebrequest)
let input = getState().input
app.models.predict(Clarifai.CELEBRITY_MODEL,
input)
.then(res => {
const data = res.outputs[0]['data']['regions'][0]['data'].concepts[0]
dispatch(fetchCelebSuccess(data))
})
.catch(err => {
const error = err.message
dispatch(fetchCelebFailed(error))
})
}
}
This is my reducer.js
import {
CHANGE_SEARCHFIELD,
REQUEST_PREDICTION_PENDING,
REQUEST_PREDICTION_SUCESS,
REQUEST_PREDICTION_FAILED
} from './constants'
const initialState = {
input: '',
imageUrl: '',
box: {},
isSignedIn: false,
isPending: false,
celeb: {},
error: '',
celebConfidence: [],
user: {
id: '',
name: '',
email: '',
entries: 0,
joined: ''
}
}
export const handleSearch = (state=initialState, action={}) => {
switch (action.type) {
case CHANGE_SEARCHFIELD:
return { ...state, input: action.payload }
default:
return state
}
}
export const requestPrediction = (state=initialState, action={}) => {
switch(action.type) {
case REQUEST_PREDICTION_PENDING:
return {...state, isPending: true}
case REQUEST_PREDICTION_SUCESS:
return {...state, celebName: action.payload, isPending: false}
case REQUEST_PREDICTION_FAILED:
return {...state, error: action.payload, isPending: false}
default:
return state
}
}
The proper way to do it is via setState since the updates my be asynchronous.
You may check out this link. How can I pass state to an action in React.js?
I am getting the response from backed which looks something like this.
But when i try to log the data like this.
render() {
const { reviews } = this.props;
console.log('rev', reviews.reviewList.data._embedded);
It gives me error saying this.
TypeError: reviews.reviewList.data is undefined
reviewDataReducer.jsx
const initialstate = {
isFetching: false,
reviewList: [],
page: null,
fetched: false,
error: null
};
export default (state = initialstate, action) => {
switch (action.type) {
case actionTypes.GET_PRODUCT_REVIEWS_LOAD:
return {
...state,
isFetching: true
};
case actionTypes.GET_PRODUCT_REVIEWS_SUCCESS:
return {
...state,
fetched: true,
reviewList: action.payload
};
case actionTypes.GET_PRODUCT_REVIEWS_ERROR:
return {
...state,
fetched: false,
isFetching: false,
error: action.error
};
default:
return state;
}
};
reviewActions.jsx
export const getProductReviews = pid => dispatch => {
console.log('rev pid',pid)
dispatch({
type: types.GET_PRODUCT_REVIEWS_LOAD
});
new _rest()
.get(`/buyer/product/${pid}/review`)
.then(res => {
console.log("Review Action Response", res);
dispatch({
type: types.GET_PRODUCT_REVIEWS_SUCCESS,
payload: res
});
})
.catch(error => {
dispatch({
type: types.GET_PRODUCT_REVIEWS_ERROR,
error: error
});
});
};
connect
const mapStateToprops = state => ({
reviews: state.reviews.data
});
const mapStateToDispatch = {
getProductReviews
};
export default connect(
mapStateToprops,
mapStateToDispatch
)(ReviewContainer);
Your information is limited but I will try to be best.
1] Error is because you'r traversing object wrong not because data in not there in this case.
2] render() {
const { reviews } = this.props;
Here I feel you mapping redux state to prop using (mapStateToProps) if so reducer is responsible how you set data in redux state.
The issue is there inside the mapStateToprops connection. Try to debug it there.
const mapStateToprops = state => {
debugger;
return ({
reviews: state.reviews.data
});
};
Open your browser console and check the value of state;
Would you mind helping me to be clear about mapDispatchToProps.
I have a example code like this:
// ----------------------- not use mapDispatchToProps -----------------------------
//var onSubmit = (event) => {
// event.preventDefault()
// var email = event.target.elements[0].value
// var password = event.target.elements[1].value
// // const path = `/repos/${userName}/${repo}`
// store.dispatch(action.requestLogin({username:email,password:password}))
// // store.dispatch(action.receiveLogin({user{username:email,password:password,objectId:1,sessionToken:"asdfg"}}))
// }
// ----------------------- use mapDispatchToProps -----------------------------
const mapDispatchToProps = (dispatch) => {
return {
onSubmit: (event) => {
event.preventDefault()
var email = event.target.elements[0].value
var password = event.target.elements[1].value
dispatch(action.requestLogin({username:email,password:password}))
}
}
}
const mapStateToProps = state => ({
// onSubmit: onSubmit,
error: state.login.error
});
var LoginPage = ({ onSubmit,error }) => {
return (
`<div className="row">
<div className="col-md-12">
<LoginFormComponent className="account-form text-center" title="Log in to Portal" error={error !== null ? error : ""} onSubmit={onSubmit}/>
</div>
</div>`
)
}
export default connect(mapStateToProps,mapDispatchToProps)(LoginPage)
//-----------------------------and this is the reducer -------------------------------------
export default function login(state = {
logedAt: null,
isLogging: false,
error: null,
data: {},
}, action) {
switch (action.type) {
case types.LOGIN_REQUEST:
return update(state, {
isLogging: { $set: true },
error: { $set: null }
});
case types.LOGIN_SUCCESS:
return update(state, {
data: { $set: action.body },
isLogging: { $set: false },
logedAt: { $set: action.logedAt },
});
case types.LOGIN_FAILURE:
return update(state, {
logedAt: { $set: null },
error: { $set: action.error },
});
default:
return state;
}
}
//-----------------------------and the middleware -------------------------------------
export function login({dispatch, getState}){
return next => action => {
return callLogin().then(
response => dispatch(Object.assign({},{
body: response,
logedAt: Date.now(),
type: LOGIN_SUCCESS,
isFetching: false,
isAuthenticated: true,
// callLogin: callLogin,
})),
error => dispatch(Object.assign({} ,{
error: error.response.text,
type: LOGIN_FAILURE,
isFetching: false,
isAuthenticated: false,
// callLogin: callLogin,
}))
);
}
}
When I don't use the mapDispatchToProps, I just can dispatch the action for type:LOGIN_REQUEST but not the LOGIN_SUCCESS,LOGIN_FAILURE, when use mapDispatchToProps, it work. could you explain for me
Thanks a lot.