Spread array of objects to array of objects react - javascript

I am using redux with react and I am trying to append my array of objects gotten from my api to my redux state which is an array of objects
This is my reducer...
import { GET_BOOKS } from "../actions/types";
const initialState = {
books: [
0:{},
1:{},
]
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
console.log(action.payload.results);
return { ...state };
default:
return state;
}
}
My api is returning
results : [
0: {somevalue},
1: {somevalue}
]
I dont know how to spread the values into a new array.

Simply assign the property and it will overwrite the old one.
export default function(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
console.log(action.payload.results);
// spread current state and inaddition to that set new books data
// which overwrites books property from old state
return { ...state, books : action.payload.results };
// spread --^^^^^^^^^^^^^^^^^^^^^^^^^^^---
default:
return state;
}
}
UPDATE : If you want to concatenate it with existing then do something like this.
export default function(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
console.log(action.payload.results);
return { ...state, books : [...state.books, ...action.payload.results] };
default:
return state;
}
}
FYI : The ...state part is for copying other state properties(assumes there exists other state values)

You need to concat current state and data coming from api
return { books: [...state.books, ...action.payload.results] };
Complete Code
import { GET_BOOKS } from "../actions/types";
const initialState = { books: [] };
export default (state: Object = initialState, action: Object) => {
switch (action.type) {
case GET_BOOKS:
return { books: [...state.books, ...action.payload.results] };
default:
return state;
}
};

Related

How to save an object in redux?

I build an app in React with Redux and I try to send to my state an object and I try to save it in 'thisUser' but I don't know how to write that 'return' because mine doesn't work.
My Redux state:
const initialState = {
thisUser: {}
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: { ...state.thisUser, ...action.payload} } //the problem
default:
return state
}
}
Dispatch method:
dispatch({ type: "users/addUser", payload: new_user });
Can you tell me how to write that return, please?
If you want to append new user then why are you using object type. You should use Array Type thisUser.
const initialState = {
thisUser: []
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser,action.payload ] }
default:
return state
}
}
Or
If you want to save only single user object then change only that line in your code:
return { ...state, thisUser: action.payload }
It's better to use an array type for if you have a list of users .
If you have a case when you need to use an object just change the brackets [ ] on my code to curly braces { } .
const initialState = {
thisUser: [],
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser, ...action.payload]}
default:
return state
}
}

adding an object element to an immutable array(javascript) [duplicate]

How do I add elements in my array arr[] of redux state in reducer?
I am doing this-
import {ADD_ITEM} from '../Actions/UserActions'
const initialUserState = {
arr:[]
}
export default function userState(state = initialUserState, action)
{
console.log(arr);
switch (action.type)
{
case ADD_ITEM:
return {
...state,
arr: state.arr.push([action.newItem])
}
default:
return state
}
}
Two different options to add item to an array without mutation
case ADD_ITEM :
return {
...state,
arr: [...state.arr, action.newItem]
}
OR
case ADD_ITEM :
return {
...state,
arr: state.arr.concat(action.newItem)
}
push does not return the array, but the length of it (docs), so what you are doing is replacing the array with its length, losing the only reference to it that you had. Try this:
import {ADD_ITEM} from '../Actions/UserActions'
const initialUserState = {
arr:[]
}
export default function userState(state = initialUserState, action){
console.log(arr);
switch (action.type){
case ADD_ITEM :
return {
...state,
arr:[...state.arr, action.newItem]
}
default:return state
}
}
If you need to insert into a specific position in the array, you can do this:
case ADD_ITEM :
return {
...state,
arr: [
...state.arr.slice(0, action.pos),
action.newItem,
...state.arr.slice(action.pos),
],
}
Since this question gets a lot of exposure:
If you are looking for the answer to this question, there is a good chance that you are following a very outdated Redux tutorial.
The official recommendation (since 2019) is to use the official Redux Toolkit to write modern Redux code.
Among other things, that will eliminate string action constants and generate action creators for you.
It will also employ methods that allow you to just write mutating logic in your Reducers created by createReducer or createSlice, so there is no need to write immutable code in Reducers in modern Redux in the first place.
Please follow the official Redux tutorials instead of third-party tutorials to always get the most up-to-date information on good Redux practices and will also show you how to use Redux Toolkit in different common scenarios.
For comparison, in modern Redux this would look like
const userSlice = createSlice({
name: "user",
initialState: {
arr:[]
},
reducers: {
// no ACTION_TYPES, this will internally create a type "user/addItem" that you will never use by hand. You will only see it in the devTools
addItem(state, action) {
// you can use mutable logic in createSlice reducers
state.arr.push(action.payload)
}
}
})
// autogenerated action creators
export const { addItem } = slice.actions;
// and export the final reducer
export default slice.reducer;
If you want to combine two arrays, one after another then you can use
//initial state
const initialState = {
array: [],
}
...
case ADD_ARRAY :
return {
...state,
array: [...state.array, ...action.newArr],
}
//if array = [1,2,3,4]
//and newArr = [5,6,7]
//then updated array will be -> [1,2,3,4,5,6,7]
...
This Spread operator (...) iterates array element and store inside the array [ ] or spreading element in the array, what you can simply do using "for loop" or with any other loop.
I have a sample
import * as types from '../../helpers/ActionTypes';
var initialState = {
changedValues: {}
};
const quickEdit = (state = initialState, action) => {
switch (action.type) {
case types.PRODUCT_QUICKEDIT:
{
const item = action.item;
const changedValues = {
...state.changedValues,
[item.id]: item,
};
return {
...state,
loading: true,
changedValues: changedValues,
};
}
default:
{
return state;
}
}
};
export default quickEdit;
The easiest solution to nested arrays is concat():
case ADD_ITEM:
state.array = state.array.concat(action.paylod)
return state
concat() spits out an updated array without mutating the state. Simply set the array to the output of concat() and return the state.
This worked for me
//Form side
const handleSubmit = (e) => {
e.preventDefault();
let Userdata = { ...userdata, id: uuidv4() };
dispatch(setData(Userdata));
};
//Reducer side
const initialState = {
data: [],
};
export const dataReducer = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.SET_DATA:
return { ...state, data: [...state.data, action.payload] };
default:
return state;
}
};

React-Redux: How to change the initial state in Reducer function

I am working on a React application and I am using Redux to store the state. I have the following code:
menu.reducer.js:
import { GET_MENU } from './menu.types';
const INITIAL_STATE = []
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_MENU:
return [ ...action.payload ];
default:
return state;
}
}
menu.actions.js:
import { apiUrl, apiConfig } from '../../util/api';
import { GET_MENU } from './menu.types';
export const getMenu = () => async dispatch => {
const response = await fetch(`${apiUrl}/menu`);
if (response.ok) {
const menuData = await response.json();
dispatch({ type: GET_MENU, payload: menuData })
}
}
In the above Reducer, the initial state is an empty array. Dispatching the GET_MENU action, changes the initial state so that it contains an array of menu items instead.
The array that is fetched in the GET_MENU action is of the following:
However I want my initial state to be like the following:
const INITIAL_STATE = {
menuArray = [],
isSending = false
}
In the GET_MENU case in the reducer code, I am not sure what the correct syntax is to use in order to assign the menuArray property in the state to the array that is returned from the GET_MENU action.
Any insights are appreciated.
The state is simply a JavaScript value. If you want it to be an object with two properties, this isn't the right syntax:
const INITIAL_STATE = {
menuArray = [],
isSending = false
}
This is:
const INITIAL_STATE = {
menuArray: [],
isSending: false
}
Your reducer will now need to also return objects. You'll want to return a new object each time. Here's how you can do your reducer, specifically:
import { GET_MENU } from './menu.types';
const INITIAL_STATE = {
menuArray: [],
isSending: false
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_MENU:
return { ...state, menuArray: [...action.payload] };
default:
return state;
}
}
This says "create an object comprised of all the properties of the previous state but with the menuArray property set to the payload."
import { GET_MENU } from './menu.types';
const initialState= {
menuArray: [],
isSending: false
}
export default (state = initialState, action) => {
switch (action.type) {
case GET_MENU:
return {...state, menuArray: action.payload};
default:
return state;
}
}
import { GET_MENU } from './menu.types';
const INITIAL_STATE = {
menuArray: [],
isSending: false
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_MENU:
return {
...state,
menuArray: action.payload
};
default:
return state;
}
}

Is this redux reducer OK

Is this reducer OK:
function someReducer(state = initialState, action) {
if (action.type === SOME_ACTION) {
const newState = Object.assign( {}, state );
// ...
// doing whatever I want with newState
// ...
return newState;
}
return state;
}
and if is OK, why we need all those immutable libraries to complicate our lives.
p.s
Just trying to comprehend Redux and immutability
export default function (state = initialState, action) {
const actions = {
SOME_ACTION: () => {
return {
...state
}
},
ANOTHER_ACTION: () => {
return {
...state
error: action.error
}
},
DEFAULT: () => state;
}
return actions[action.type] ? actions[action.type]() : actions.DEFAULT();
}
I prefer doing this instead. I am not a big fan of switch statements.
The standard approach is to use a switch/case with spread syntax (...) in your reducer.
export default function (state = initialState, action) {
switch (action.type) {
case constants.SOME_ACTION:
return {
...state,
newProperty: action.newProperty
};
case constants.ERROR_ACTION:
return {
...state,
error: action.error
};
case constants.MORE_DEEP_ACTION:
return {
...state,
users: {
...state.users,
user1: action.users.user1
}
};
default:
return {
...state
}
}
}
You can then use ES6 spread syntax to return your old state with whatever new properties you want changed/added to it.
You can read more about this approach here...
https://redux.js.org/recipes/using-object-spread-operator
I found something that I really like:
import createReducer from 'redux-starter-kit';
const someReducer = createReducer( initialState, {
SOME_ACTION: (state) => { /* doing whatever I want with this local State */ },
SOME_ANOTHER_ACTION: (state) => { /* doing whatever I want with local State */ },
THIRD_ACTION: (state, action) => { ... },
});
If your state has nested objects or arrays, Object.assign or ... will copy references to your older state variable and it may cause some issue. This is the reason why some developers use immutable libraries as in most of the case state has deep nested array or objects.
function someReducer(state = initialState, action) {
if (action.type === SOME_ACTION) {
const newState = Object.assign( {}, state );
// newState can still have references to your older state values if they are array or orobjects
return newState;
}
return state;
}

redux - how to store and update a key/value pair

I am using redux wth reactjs.
I want to store simple key/value pairs but can't get the reducer syntax right.
In this case each key/value pair will hold a connection to an external system.
Is this the right way to do it? I'm at the beginning with redux so it's a bit of mystery.
export default (state = {}, action) => {
switch(action.type) {
case 'addConnection':
return {
connections: {
...state.connections, {
action.compositeKey: action.connection
}
}
default:
return state
}
}
This worked for me:
export default (state = {}, action) => {
switch(action.type) {
case 'addConnection':
return {
...state,
connections: {
...state.connections,
[action.compositeKey]: action.connection
}
}
default:
return state
}
}
From the docs:
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#correct-approach-copying-all-levels-of-nested-data
You just have a couple mistakes with {} instead of [] and forgetting to use Object.assign.
const reducer = (state = {}, action) => {
switch (action.type) {
case 'addConnection':
return Object.assign({}, state, {
connections: [
...state.connections,
{
[actions.compositeKey]: action.connection
}
]
});
default:
return state;
}
}
export default reducer;
It might help to see it expressed this way too. It does the same thing but I think it reads a little nicer
const reducer = (state = {}, {type, compositeKey, connection}) => {
switch (type) {
case 'addConnection':
return Object.assign({}, state, {
connections: state.connections.concat({
[compositeKey]: connection
})
});
default:
return state;
}
}
export default reducer;
Or if you're using Immutable, something like this
import Immutable from 'immutable';
const reducer = (state = Immutable.Map(), {type, compositeKey, connection}) => {
switch (type) {
case 'addConnection':
return state.set(
'connections',
state.get('connections').concat({
[compositeKey]: connection
})
);
default:
return state;
}
}
export default reducer;
This may work
const reducer = (state = {}, {type, compositeKey, connection}) => {
switch (type) {
case 'addConnection':
var newData={};
newData[compositeKey]=connection;
return Object.assign({}, state, newData)
});
default:
return state;
}
}
export default reducer;

Categories

Resources