Redux store Array to Div list of elements - reactjs

I have a list of object stored as a array in my redux store which loads on component mount. I want to List them in a div, also to do the crud Operation. This is my implementation. Whenever I use useSelector to save the list for a constants it fectching infinite number of logs.
BranchAction.js
import axios from 'axios';
export const fetchAllBranchListOk = (branchList) => {
return {
type : 'FETCH_ALL_BRANCH_LIST_OK',
branchList
}
};
export const fetchAllBranchList = () =>{
return (dispatch) => {
return axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then(response => {
dispatch(fetchAllBranchListOk(response.data));
})
.catch(error => {
throw(error);
});
}
};
BranchReducer
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_ALL_BRANCH_LIST_OK' :
return action.branchList;
default:
return state;
}
};
BranchManagement.js
function BranchManagement() {
store.dispatch(BranchAction.fetchAllBranchList());
const AllBranch = useSelector(state => state.BranchReducer)
return(
<div>
</div>
)
}
export default BranchManagement;
CombinedReducer -> index.js
import {combineReducers} from 'redux'
import BranchReducer from "./Admin/BranchReducer";
const Reducers = combineReducers({
BranchReducer
});
export default Reducers;

If you want to dispatch the action to fetch the data from the backed, you should be keeping those calls in useEffect hook. The purpose of useEffect is similar to the purpose of Lifecycle methods in the class component like componentDidMount, componentDidUpdate and componentWillUnMount. To understand more about useEffect please refer this.
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import BranchAction from "/path/to/BranchAction";
function BranchManagement() {
const dispatch = useDispatch();
//Since the data in the state is on `branchList`. You can directly return
//`state.branchList` which will you provide you the data you are looking for.
const branchList = useSelector(state => state.branchList)
//It'll act similar to `componentDidMount`. Since we are passing `[]`
//to `useEffect` dependencies array
useEffect(() => {
dispatch(BranchAction.fetchAllBranchList());
}, [])
//Here I'm assuming `branchList` is array of objects with `name` and `id`.
//Updated answer with branchList as[{"branchID":1,"createdBy":1,"isActive":true,"branchDetails":{"branchDetailsID":1}},{"branchID":2,"createdBy":1,"isActive":true,"branchDetails":{"branchDetailsID":1}}]
return(
<div>
{
(branchList || []).map((branch, index) => {
<div key={branch.branchID || index}>
<span>{branch.branchID}</span>
<span>{branch.createdBy}</span>
<span>{branch.isActive}</span>
<span>{branch.branchDetails.branchDetailsID}</span>
</div>
}
}
</div>
)
}
export default BranchManagement;
Hope this helps in order to resolve the issue.

Related

How can multiple action items be dispatched within a function while using redux-saga as the middleware?

I am using redux-saga as a middleware and i want to dispatch multiple action items to the store. As of now i am able to dispatch only one action (i.e., within the fetchData() function which i am calling in Home.js component).
I've tried adding multiple actions but its not working, Only the first action type is getting dispatched
action.js
import { FETCH_ABOUT, FETCH_CTA, FETCH_PRODUCTS} from './actionType'
//import axios from 'axios';
export const fetchData = () => (
{ type:FETCH_PRODUCTS}
)
export const fetchProducts = (products) => ({
type: FETCH_PRODUCTS,
payload: products,
})
export const fetchCta = (cta) => ({
type: FETCH_CTA,
payload: cta,
})
export const fetchAbout = (about) => ({
type: FETCH_ABOUT,
payload: about,
})
reducer.js
import { FETCH_ABOUT, FETCH_CTA, FETCH_PRODUCTS } from "./actionType"
const initialState = {
products: [],
cta:'',
about:'',
}
const productsReducer = (state=initialState,action) => {
switch(action.type){
case FETCH_PRODUCTS:
return{
...state,
products: action.payload
}
case FETCH_CTA:
return{
...state,
cta: action.payload
}
case FETCH_ABOUT:
return{
...state,
about: action.payload
}
default:
return state;
}
}
export default productsReducer;
ProductsSaga.js
import {call,fork,put,takeLatest} from 'redux-saga/effects'
import { fetchCta, fetchProducts } from '../redux/action';
import { FETCH_CTA, FETCH_PRODUCTS } from '../redux/actionType';
import { fetchAPIcall } from '../redux/api'
function* fetchData() {
try{
const { data } = yield call(fetchAPIcall);
console.log(data.data.productCopy);
yield put(fetchProducts(data.data.productCopy));
}catch(e){
console.log(e);
}
}
//watcher saga
export function* watcherSaga() {
yield takeLatest(FETCH_PRODUCTS,fetchData)
}
export const productsSaga = [fork(watcherSaga)]
ctaSaga.js
import { call,put,takeLatest,fork } from "redux-saga/effects";
import { fetchCta } from "../redux/action";
import { FETCH_CTA } from "../redux/actionType";
import { fetchAPIcall } from "../redux/api";
function* onFetchCta() {
try{
const { data } = yield call(fetchAPIcall);
console.log(data.data.cta);
yield put(fetchCta(data.data.cta));
}catch(e){
console.log(e);
}
}
//watcher saga
export function* watcherSaga() {
yield takeLatest(FETCH_CTA,onFetchCta)
}
export const ctaSaga = [fork(watcherSaga)]
Home.js
import React, { useEffect, useState } from 'react'
import './Home.css'
import {useHistory} from 'react-router-dom';
import { useSelector,useDispatch } from 'react-redux';
import {fetchData} from './redux/action'
const Home = () => {
const history = useHistory();
const {products,cta} = useSelector((state)=>(state.productsReducer));
console.log(products,cta);
const dispatch = useDispatch();
useEffect(()=>{
dispatch(fetchData());
},[])
const productDetail = (item,i) => {
history.push({
pathname:`product-detail/${i}`,
state:item
})
}
return (
<div className='container'>
<div className='product'>
{products.map((item,i) =>{
return(
<div key={item.id}>
<img src={item.Image.path} alt = {item.Image.alt}/>
<p>{item.title}</p>
<button onClick={()=>productDetail(item,i)}type='button'>{cta}</button>
</div>
)
})}
</div>
</div>
)
}
export default Home
Action creators such as fetchData will always create only a single action object. Also the dispatch function (or put effect) can always dispatch only a single action, however nothing is preventing you from dispatching multiple actions one after the other:
function* mySaga() {
yield put(firstAction())
yield put(secondAction())
yield put(thirdAction())
}
// or
function MyComponent() {
const dispatch = useDispatch()
return <div onClick={() => {
dispatch(firstAction())
dispatch(secondAction())
dispatch(thirdAction())
}}>Hello</div>
}
If you are worried about rerenders, react-redux has the batch function that allows you to wrap your dispatches making sure that react batches all the updates and rerenders only a single time. Note that this is not necessary starting from React 18 as it batches things automatically.
It is more difficult to use the batched updates with redux saga due to its internal scheduler that doesn't guarantee that everything will happen in single tick, but again starting from React 18 you don't need to worry about this. In case you really need it, there are libraries out there that allows you to do it though, check out Mark's post about it which includes links to some of these libraries: https://blog.isquaredsoftware.com/2020/01/blogged-answers-redux-batching-techniques/
One more thing, if you find yourself dispatching the same list of actions again and again, maybe it make sense to merge these together to a single action and avoid the issue entirely.

react cannot put parameter in function (redux)

rootReducer
import { combineReducers } from "redux";
import mods from "./mods.js";
export default combineReducers({
mods
})
reducers/mods.js
import { GET_MODS, GET_SPECIFC_MOD } from "../actions/types"
const initialState = {
mods: [],
currMod: []
}
export default function(state = initialState, action) {
switch(action.type) {
case GET_MODS:
return {
...state,
mods: action.payload
}
case GET_SPECIFC_MOD:
return {
...state,
currMod: action.payload
}
default:
return state
}
}
actions/mods.js
import axios from 'axios'
import { GET_MODS, GET_SPECIFC_MOD } from './types'
// get the mods
export const getMods = () => dispatch => {
axios.get('http://localhost:8000/api/mods')
.then(res => {
dispatch({
type: GET_MODS,
payload: res.data
})
}).catch(err => console.log(err))
}
// get single mod
export const getSpecificMod = (title) => dispatch => {
axios.get(`http://localhost:8000/api/mods/${title}`)
.then(res => {
dispatch({
type: GET_SPECIFC_MOD,
payload: res.data
})
}).catch(err => console.log(err))
}
components/download.js
import React from 'react'
import { useState, useEffect } from 'react'
import { connect } from 'react-redux'
import { getSpecificMod } from '../actions/mods'
const Download = () => {
useEffect(() => {
const title = window.location.pathname.split('/')[3]
getSpecificMod(title)
})
return (
<></>
)
}
const mapStateToProp = state => ({
currMod: state.mods.currMod
})
export default connect(mapStateToProp, getSpecificMod)(Download)
Response from backend
GET http://localhost:8000/api/mods/function(){return!window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__&&a.dispatch.apply(a,arguments)}
Basically the user clicks on a mod and gets sent to the download section that is handled by 'download.js' the component ('download.js') renders it and reads the window.location to retrieve the title, with redux I want to get the mod so i made a function that takes the title and sends the request 'getMod(title)' but for some reason it is throwing horrible errors that I dont understand, any help is appreciated!
You are not dispatching the action properly in your component. Right now you are actually just calling the getSpecificMod action creator function from your imports. Your Download component doesn't read anything from props so it is ignoring everything that gets created by the connect HOC.
If you want to keep using connect, you can fix it like this:
import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import { getSpecificMod } from '../actions/mods'
const Download = ({currMod, getSpecificMod}) => {
const title = window.location.pathname.split('/')[3]
useEffect(() => {
getSpecificMod(title)
}, [title])
return (
<></>
)
}
const mapStateToProps = state => ({
currMod: state.mods.currMod
})
export default connect(mapStateToProps, {getSpecificMod})(Download)
We are now accessing the bound action creator as a prop of the component. mapDispatchToProps is an object which maps the property key to the action.
But it's better to use the useDispatch hook:
import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getSpecificMod } from '../actions/mods'
const Download = () => {
const currentMod = useSelector(state => state.mods.currMod);
const dispatch = useDispatch();
const title = window.location.pathname.split('/')[3]
useEffect(() => {
dispatch(getSpecificMod(title));
}, [title, dispatch]);
return (
<></>
)
}
export default Download;
There might be some confusion on terminology here. Your getSpecificMod function is a function which takes dispatch as an argument but it is not a mapDispatchToProps. It is a thunk action creator.
Make sure that you have redux-thunk middleware installed in order to handle this type of action. Or better yet, use redux-toolkit.
Your useEffect hook needs some sort of dependency so that it knows when to run. If you only want it to run once you can use an empty array [] as your dependencies. If you don't specify the dependencies at all then it will re-run on every render.
Does the pathname change? If so, how do you know when? You might want to add an event listener on the window object. Or consider using something like react-router. But that is a separate question.

React redux not fetching data from API

Hi im new to redux and im trying to create a movie app using the API from www.themoviedb.org. I am trying to display the popular movies and im sure the API link works since ive tested it in postman but i cant seem to figure out why redux doesnt pick up the data.
//action
import { FETCH_POPULAR } from "./types";
import axios from "axios";
export const fetchPopularMovies = () => (dispatch) => {
axios
.get(
`https://api.themoviedb.org/3/movie/popular?api_key=${API}&language=en-US`
)
.then((response) =>
dispatch({
type: FETCH_POPULAR,
payload: response.data
})
)
.catch((err) => console.log(err));
};
//reducer
import { FETCH_POPULAR } from "../actions/types";
const initialState = {
popular: [],
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_POPULAR:
return {
...state,
popular: action.payload,
};
default:
return state;
}
}
import React from "react";
import { connect } from "react-redux";
import Popular from "./Popular";
const FetchedPopular = (props) => {
const { popular } = props;
let content = "";
content =
popular.length > 0
? popular.map((item, index) => (
<Popular key={index} popular={item} />
))
: null;
return <div className="fetched-movies">{content}</div>;
};
const mapStateToProps = (state) => ({
popular: state.popular.popular,
});
export default connect(mapStateToProps)(FetchedPopular);
import React from "react";
import "../Styles.css";
const Popular = (props) => {
return (
<div className="movie-container">
<img
className="poster"
src={`https://image.tmdb.org/t/p/w400/${props.poster_path}`}
/>
</div>
);
};
export default Popular;
I cant really tell what I'm missing can someone help?
Next to mapStateToProps you need to create mapDispatchToProps. After that, you will be able to call your Redux action from your React component.
I suggest you the mapDispatchToProps as an Object form. Then you need to use this mapDispatchToProps as the second parameter of your connect method.
When you will have your action mapped to your component, you need to call it somewhere. It is recommended to do it for example on a component mount. As your React components are Functional components, you need to do it in React useEffect hook.
import React, { useEffect } from "react";
import { connect } from "react-redux";
import Popular from "./Popular";
import { fetchPopularMovies } from 'path_to_your_actions_file'
const FetchedPopular = (props) => {
const { popular } = props;
let content = "";
useEffect(()=> {
// call your mapped action (here it is called once on component mount due the empty dependency array of useEffect hook)
props.fetchPopularMovies();
}, [])
content =
popular.length > 0
? popular.map((item, index) => (
<Popular key={index} popular={item} />
))
: null;
return <div className="fetched-movies">{content}</div>;
};
const mapStateToProps = (state) => ({
popular: state.popular.popular,
});
// create mapDispatchToProps
const mapDispatchToProps = {
fetchPopularMovies
}
// use mapDispatchToProps as the second parameter of your `connect` method.
export default connect(mapStateToProps, mapDispatchToProps)(FetchedPopular);
Moreover, as I wrote above in my comment, your Popular does not have the prop poster_path but it has the prop popular which probably has the property poster_path.
import React from "react";
import "../Styles.css";
const Popular = (props) => {
return (
<div className="movie-container">
<img
className="poster"
src={`https://image.tmdb.org/t/p/w400/${props.popular.poster_path}`}
/>
</div>
);
};
export default Popular;

state.cartItems is not iterable when i want to add a item to cart

I'm trying to learn mern stack and redux by making a small e-commerce website and i have this state.cartItmes is not iterable error in redux action and i have no idea what is causing it or how to fix it
So because i hope one day i will became a Frontend developer i'm doing what a developer does...i'm asking you guys what i did wrong in my spagetti code
Cart Reducer
export const cartReducer = (state = { cartItems: [] }, action) => {
if (action.type === "ADD_TO_CART") {
return { cartItems: [...state.cartItems, action.payload] };
}
return state;
};
Cart Action
import axios from "axios";
export const addToCart = (id) => async (dispatch, getState) => {
try {
const { data } = await axios.get(`/products/${id}`);
dispatch({ type: "ADD_TO_CART", payload: data });
localStorage.setItem(
"cartItems",
JSON.stringify(getState().cart.cartItems)
);
} catch (error) {
console.log(error.message);
}
};
Cart component
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addToCart } from "../actions/CartActions";
import { useParams } from "react-router-dom";
const Cart = () => {
const dispatch = useDispatch();
const { id } = useParams();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
console.log(cartItems);
useEffect(() => {
dispatch(addToCart(id));
}, [dispatch, id]);
return (
<section>
<div className="cart">
<div className="cart-items">Cart</div>
<div className="buy-items"></div>
</div>
</section>
);
};
export default Cart;
After analyzing your code, I think the issue may be the empty array that you initially set. You should try using some default value as initial value, I think this should solve the issue.
Also, it is generally not a good practice to put async logic inside a reducer. You may want to implement some middleware for this. Here's some more info on it: https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-middleware-to-enable-async-logic

React-Redux State won't update onClick

New to React. I am trying out react redux for the first time (on my own). I have a state for a gameboard called force_hidden that I want to set in App.js and then use in a child component ( a few levels down). I used redux to create forceGameBoardHidden that should set force_hidden to whatever value is inside the (). so, forceGameBoardHidden(true) should set the state of force_hidden to true. However, that doesn't happen. I can click on the item and it logs "before change" and then the state. In between it should have set the state to true, but the state is still false. I don't know what's going wrong here. I tried console.logging the gameBoardReducer. It fires when I start the page, but doesn't fire when I click the button.
gameboard.types.js
const GameBoardActionTypes = {
FORCE_GAMEBOARD_HIDDEN: 'FORCE_GAMEBOARD_HIDDEN'
}
export default GameBoardActionTypes;
gameboard.action.js
import GameBoardActionTypes from './game-board.types';
export const forceGameBoardHidden = value => ({
type: GameBoardActionTypes.FORCE_GAMEBOARD_HIDDEN,
payload: value
});
gameboard.reducer.js
import GameBoardActionTypes from './game-board.types'
const INITIAL_STATE = {
force_hidden: false
}
const gameBoardReducer = ( state = INITIAL_STATE, action) => {
switch (action.type) {
case GameBoardActionTypes.FORCE_GAMEBOARD_HIDDEN:
return {
...state,
force_hidden: action.payload
}
default:
return state;
}
}
export default gameBoardReducer;
root-reducer
import { combineReducers } from 'redux';
import gameBoardReducer from './game-board/game-board.reducer'
export default combineReducers ({
gameboard: gameBoardReducer
})
store.js
const middlewares = [];
const store = createStore(rootReducer, applyMiddleware(...middlewares))
export default store;
index.js
<Provider store={store}>
App.js -- this is where the magic should happen in forceGameBoardHidden
const App = () => {
const handleKeyChange = event => {
setKey(event.target.value);
console.log("before change")
forceGameBoardHidden(true)
console.log(store.getState().gameboard)
}
return (
<SearchBox
onChange={handleKeyChange}
placeholder="Enter your game Key"/>
</div>
);
}
const mapDispatchToProps = dispatch => ({
forceGameBoardHidden: item => dispatch(forceGameBoardHidden(item))
})
export default connect(null,mapDispatchToProps)(App);
I think you need to dispatch the action, there are 2 methods , one is to connect the component to the actions and bind them to dispatch. The other one is much easier since you use functional components, is by using the useDispatch hook
Example here:
import { useDispatch } from 'react-redux' // <-- add this
const App = () => {
const dispatch = useDispatch() // <-- add this
const handleKeyChange = event => {
setKey(event.target.value);
console.log("before change")
dispatch(forceGameBoardHidden(true)) // <-- change this
console.log(store.getState().gameboard)
}
return (
<SearchBox
onChange={handleKeyChange}
placeholder="Enter your game Key"/>
</div>
);
}

Resources