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.)
Related
The totalItems returns value inside the useEffect on first render but returns undefined when refreshed making setPageCounter return undefined. Why is this happening and what is the solution?
const Products = () => {
const dispatch = useDispatch();
const { products, loading, totalItems } = useSelector(
(state) => state.products
);
const [searchParams, setSearchParams] = useSearchParams();
const params = Object.fromEntries([...searchParams]);
console.log(totalItems); // Returns the value after refreshing the page
// Pagination
const [currentPage, setCurrentPage] = useState(1);
const [pageCounter, setPageCounter] = useState(0);
const itemsPerPage = 8;
useEffect(() => {
if (Object.keys(params).length > 0) {
dispatch(searchProduct(params));
} else {
dispatch(getProduct(currentPage));
console.log(totalItems); // Returns undefined after refreshing the page
}
}, [dispatch, currentPage]);
};
Here is the Product Reducer code
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
ALL_PRODUCT_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from "../constants/productConstants";
export const productReducer = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCT_REQUEST: return {
...state, loading: true, products: []
}
case ALL_PRODUCT_SUCCESS: return {
...state,
loading: false,
products: action.payload.data.data,
perPage: action.payload.data.productsperpage,
pageNumber: action.payload.data.pageNumber,
totalItems: action.payload.data.totalItems
}
case ALL_PRODUCT_FAIL: return {
...state,
loading: false,
error: action.payload
}
case CLEAR_ERRORS: return {
...state,
error: null
}
default: return state;
}
};
Here is the Product Action code
import axios from 'axios';
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
ALL_PRODUCT_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from '../constants/productConstants';
export const getProduct = (page) => async (disptach) => {
try {
disptach({ type: ALL_PRODUCT_REQUEST });
const data = await axios.get(`/api/products?page=${page}`);
disptach({
type: ALL_PRODUCT_SUCCESS,
payload: data,
});
} catch (error) {
disptach({
type: ALL_PRODUCT_FAIL,
payload: error.response.data.message,
});
}
}
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 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;
I'm new to Redux. And I'm trying to create a simple FETCH_ALL_POSTS.
actions
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
console.log(response.data)
dispatch({
type: FETCH_ALL_POSTS,
payload: response.data
})
}
posts reducer
export default (state = {}, action) => {
const { type, payload } = action;
switch (type) {
case FETCH_ALL_POSTS:
return {
...state, payload
}
default:
return state
}
}
post list component
const mapStateToProps = state => {
console.log(Object.values(state.posts))
return {
posts: state.posts
}
}
This is working but the data that I'm getting from mapStateToProps is not what I'm expecting.
Result : "array: [ 0:[{},{},{}] ]"
My expected result: "array:[{},{},{}]"
Try this,
const initialState = {
posts: '',
}
export default (state=initialState, action) => {
const { type, payload } = action;
switch (type) {
case FETCH_ALL_POSTS:
return{
posts:state.posts=action.payload.posts
}
default:
return state
}
}
I create app with react and redux and I need to fetch data. Is there any way to reuse function getData() and reducer. My actions looks like this
importing constants
const getDataRequested = () => {
return {
type: GET_DATA_REQUESTED
};
}
const getDataDone = data => {
return {
type: GET_DATA_DONE,
payload: data
};
}
const getDataFailed = () => {
return {
type: GET_DATA_FAILED
};
}
export const getData = () => dispatch => {
dispatch(getDataRequested());
fetch('url')
.then(response => response.json())
.then(data => {
dispatch(getDataDone(data));
})
.catch(error => {
dispatch(getDataFailed(error));
})
}
and reducer
importing constants
const initialState = {
isLoading: false,
isError: false,
data: [],
}
export default (state=initialState, action) => {
switch (action.type) {
case GET_DATA_REQUESTED:
return { ...state, isLoading: true };
case GET_DATA_DONE:
return { ...state, isLoading: false, data: action.payload };
case GET_DATA_FAILED:
return { ...state, isLoading: false, isError: true}
default:
return state;
}
};
Every time I fetch something with different url I create new action and new reducer. Is it ok or there is some way to reuse it?
You can pass a url parameter to your thunk. So, you could have something like this:
export const getData = (url) => dispatch => {
dispatch(getDataRequested());
fetch(url)
.then(response => response.json())
.then(data => {
dispatch(getDataDone(data));
})
.catch(error => {
dispatch(getDataFailed(error));
})
}
This way you can dispatch as many actions as you want changing only the url parameter, like this: getData('/user'), getData('/products').
You can also customize the way you store the state into redux by passing more parameters to the thunk. So it could be something like this:
const getDataDone = data => {
return {
type: GET_DATA_DONE,
payload: data
};
}
export const getData = (url, stateName) => dispatch => {
dispatch(getDataRequested());
fetch(url)
.then(response => response.json())
.then(data => {
dispatch(getDataDone({ stateName: data }));
})
.catch(error => {
dispatch(getDataFailed(error));
})
}
And the reducer could be something like this:
const initialState = {
isLoading: false,
isError: false,
data: {},
}
export default (state=initialState, action) => {
switch (action.type) {
case GET_DATA_REQUESTED:
return { ...state, isLoading: true };
case GET_DATA_DONE:
return { ...state, isLoading: false, [action.payload.stateName]: action.payload.data };
case GET_DATA_FAILED:
return { ...state, isLoading: false, isError: true}
default:
return state;
}
};
That way you can dispatch actions like getData('/user', 'user') or getData('/products', 'products') and have a state like this:
{
user: {
// your users data
},
products: {
// your products data
}
}
You can combine all actions in one function like
function getData() {
return {
types: [GET_DATA_REQUESTED, GET_DATA_DONE, GET_DATA_FAILED],
callApi: fetch('/')
}
}
but you need to connect your component and pass the function as props
function mapDispatchToProps(dispatch) {
return {
getData: () => dispatch(getData())
};
}
connect(null, mapDispatchToProps)(YourComponent)
now you can use the function in your component and it will return a promise.
check out the Docs for redux: https://github.com/reactjs/react-redux