redux dispatch action on every element of an array - reactjs

I have an array of x element
Arr (2) ['16', '149']
I need to dispatch an action on every Id in this array in order to find Object by Id.
My reducer for single Id is working and it is returning me value. But im lost.
This is my reducer
const initialState = {
product: {},
};
const productReducer = (state = initialState, action) => {
switch (action.type) {
case GET_PRODUCT_BY_ID:
return {
...state,
product: action.payload,
};
default:
return state;
}
};
my action:
export const getProductById = (Id)=>async(dispatch)=>{
try {
const {data}= await api.getProductById(Id)
dispatch({type:GET_PRODUCT_BY_ID, payload: data})
} catch (error) {
console.log(error)
}
}
I would like to return something like array of objects with values for that id. Im stuck with this.

Related

Why my reducer is not changing synchronous?

I have a reducer that seems me async by default. I had read in alot o places that the dispatch is synchronous by default, but in this case the console.log always print an empty array in the first call of my function, and in the second call it prints with one element instead of two. I don't know if I'm doing any mistake.
const answerQuestion = (correctAnswer, answerText) => {
const {
interview: { questionGroups, responses },
} = state;
dispatch({
type: ANSWER_CURRENT_QUESTION,
payload: {
correctAnswer,
answerText,
},
});
console.log(responses);
};
And I have this reducer
export default (state, action) => {
switch (action.type) {
case ANSWER_CURRENT_QUESTION:
return {
...state,
interview: {
...state.interview,
responses: [...state.interview.responses, action.payload],
},
};
default:
return state;
}
};

react-redux store data - reducer setter

I'm trying to store a dictionary in react-redux in react-native.
So my action looks like this :
let data = {};
export const setData = (pData) => ({
type: 'SET',
data: pData,//I don't know how to store the data in data declared in parent
});
export const getData = () => ({
type: 'GET',
data: data,
});
And my reducer looks like this :
const items = (state = [], action) => {
switch (action.type) {
case 'SET':
return [
//I don't know how to set the data here
];
case 'GET':
return state;
default:
return null;
}
};
export default items;
I looked in many tutorial on YouTube, they just you need to paste this, and boom.
If I get cleared with one dictionary, I think I can work with others.
This part almost right. You don't need "GET" to get data and this part let data = {} should be in reducer;
export const setData = (pData) => ({
type: 'SET',
data: pData,
});
/*
export const getData = () => ({
type: 'GET',
data: data,
});
*/
Reducer
const initState = {
data:[],
anotherSate:[]
}
const rootReducer = (state = initState, action) => {
switch(action.type){
case 'SET': {
return {
...state, // if you have more states
data: [action.data, ...state.data]
}
}
default:
return state;
}
}
export default rootReducer;
You can get your "Data". "New" component
//Your component code
//...
this.props.data // here is your "data"
//...
const mapStateToProps = (state) => {
return {
data: state.data,
}
}
export default connect(mapStateToProps)(NewComponent);
In order to check if your Reducer works, try to add something in your initState and extract the data in NewComponent
const items = (state = [], action) => {
switch (action.type) {
case 'SET':
return {
...state, // adding the previous state first
data: action.data // here data can be any keyword you want to save your dictionary in
}
case 'GET':
return state;
default:
return null;
}
};

Setting the initial state of redux in reducers

Hey guys i am stuck in a situation in which i have to set the inital state of reducer to some value let me show you the code
First of all i have an action creater like this
export const fetchuser = () => {
return async dispatch => {
const res = await axios.get("/api/currentuser");
dispatch({
type: "fetchuser",
payload: res.data
});
};
};
which just fetches the data from api and dispatches an action to reducer
export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
return action.payload||false;
default:
return state;
}
}
now in second action creater i have to make a post request and increase the "credits" value in user database
export const handletoken = token => {
return async dispatch => {
const res = await axios.post("/api/stripe", token);
dispatch({ type: "credits", payload: res.data });
};
};
so i get the updated value here then i pass this on to the reducer
export default function(state = {}, action) {
switch (action.type) {
case "credits":
return action.payload
default:
return state;
}
}
and then combine them in reducer/index.js
export default combineReducers({
auth: authreducer,
credits:creditsreducer
});
console log of auth reducer in app.js in mapstatetoprops function gives
auth:
credits: 40
googleid: "109463598810933991924"
__v: 0
_id: "5d7fff2c4cb0604139055ce4"
so in credits reducer as u can see i have defined initial value of state as an empty object but i want to set it as the value of credits key of auth reducer, I could easily set it to array or an object hardcoding it but here i need to set its value as a value which is already in my another reducer so how can i achieve this ?
Assuming you need to wait for "fetchuser" to succeed to set credits in your creditsreducer you can handle the "fetchuser" action in your creditsreducer as well:
export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
return action.payload ? action.payload.credits : state;
case "credits":
return action.payload
default:
return state;
}
}
Always keep previous reducer state value. Otherwise no use of redux state value. like this
1.export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
let data = action.payload||false;
return {
...state,
fetchuser: data //any where you can access fetchuser data as well as previous state will not change.
}
default:
return state;
}
}
Change all the reducers like above.

How do I return an array from a reducer?

I have an array of items. When the items update I dispatch an UPDATED_LIST action and pass along the item with its updated data.
For example:
const initialState = {
items: []
}
const reducer = items(state = initialState, action) {
switch(action.type) {
case 'UPDATED_ITEMS':
return { ...state, ...action.payload }
default:
return state
}
}
I dispatch like so:
store.dispatch({
type: 'UPDATED_ITEMS',
payload: [ { name: "bob"}, { name: "harry" } ]
})
And mapStateToProps:
const mapStateToProps = state => ({ items: state.items })
My problem is when I try to access items from within a component it's an object instead of an array. I have to do the following to get access to the array:
const mapStateToProps = state => ({
items: Object.keys(state.offers).map((k) => state.items[k])
})
Is it possible to get the items as an array without having to convert them?
In your reducer update it to where you set items with the action payload. You were previously using the spread operator on your action payload which converts all your array indexes into the state object as keys.
const reducer = items(state = initialState, action) {
switch(action.type) {
case 'UPDATED_ITEMS':
return { ...state, items: [...action.payload] }
default:
return state
}
}
If you don't want a nested state in your mapStateToProps you can do this where you make your initial state an array. Similar to the todo reducer shown here. https://redux.js.org/basics/example-todo-list#reducerstodos.js
const initialState = [];
const reducer = items(state = initialState, action) {
switch(action.type) {
case 'UPDATED_ITEMS':
return [ ...action.payload ];
default:
return state
}
}
const mapStateToProps = state => ({
items: state.items
})

Redux: Distinguish objects in reducers

I'm quite new to Redux and from what I understand, a reducer should be created for each type of object. E.g. for user interaction a user reducer should be created. My question is: How do you handle cases where you require the object for different purposes?
Scenario: Imagine having a user reducer which returns the current user. This user would be required in the entire application and needed for general controls on every page.
Now what happens when you need to load another user which is used for different purposes. E.g. profile page: loading a user to display information.
In this case there would be a conflict if the user reducer would be used. What would be the correct way to handle this in Redux? In case a different reducer would have to be created, what would be the naming convention for the new reducer?
First, you've mentioned:
a user reducer which loads the current user
I don't know if I got you correctly, but if this means you want to fetch (from an API, for example) the current user inside the reducer, this is a wrong approach.
Reducers are intended to be pure functions. You can call them with the same arguments multiple times and they will always return the same expected state.
Side effects like that should be handled by action creators, for example:
actions/user.js
export const FETCH_ME = 'FETCH_ME'
export const FETCH_ME_SUCCESS = 'FETCH_ME_SUCCESS'
// it's using redux-thunk (withExtraArgument: api) module to make an async action creator
export const fetchMe = () => (dispatch, getState, api) => {
dispatch({ type: FETCH_ME })
return api.get('/users/me').then(({ data }) => {
dispatch({ type: FETCH_ME_SUCCESS, data })
return data
})
}
Inside your reducer you can simple get the data and set a new state (note that if you send the action with the same data multiple times, the state will always be the same).
reducers/user.js
import { FETCH_ME, FETCH_ME_SUCCESS } from '../actions/user'
const initialState = {
item: null,
loading: false
}
export const userReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_ME:
return {
...state,
loading: true
}
case FETCH_ME_SUCCESS:
return {
...state,
loading: false,
item: action.data
}
default:
return state
}
}
Now, for your scenario:
Now what happens when you need to load another user which is used for different purposes. E.g. profile page: loading a user to display information.
You will just write another action creator for that:
actions/user.js
export const FETCH_ME = 'FETCH_ME'
export const FETCH_ME_SUCCESS = 'FETCH_ME_SUCCESS'
export const FETCH_USER = 'FETCH_USER'
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'
export const fetchMe = () => (dispatch, getState, api) => {
dispatch({ type: FETCH_ME })
return api.get('/users/me').then(({ data }) => {
dispatch({ type: FETCH_ME_SUCCESS, data })
return data
})
}
export const fetchUser = (id) => (dispatch, getState, api) => {
dispatch({ type: FETCH_USER })
return api.get(`/users/${id}`).then(({ data }) => {
dispatch({ type: FETCH_USER_SUCCESS, data })
return data
})
}
Then you adapt your reducer to manage more sets:
reducers/user.js
import { combineReducers } from 'redux'
import { FETCH_ME, FETCH_ME_SUCCESS, FETCH_USER, FETCH_USER_SUCCESS } from '../actions/user'
const initialState = {
item: null,
loading: false
}
const meReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_ME:
case FETCH_ME_SUCCESS:
return userReducer(state, action)
default:
return state
}
}
const activeReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_USER:
case FETCH_USER_SUCCESS:
return userReducer(state, action)
default:
return state
}
}
const userReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_USER:
case FETCH_ME:
return {
...state,
loading: true
}
case FETCH_USER_SUCCESS:
case FETCH_ME_SUCCESS:
return {
...state,
loading: false,
item: action.data
}
default:
return state
}
}
export default combineReducers({
activeUser: activeReducer,
me: meReducer
})
Your final user state should be something like:
{
me: {
item: null,
loading: false
},
active: {
item: null,
loading: false
}
}

Resources