Handle state update object - javascript

I am using redux for my application's state but I find it hard to update the state correctly when it is nested.
As you can see I use the ...state in my reducer but is there a way to use this when I only need to update a key of a child object and keep the rest of the state ? See below for example
// initial state
state = {
isFetching: true,
isUpdating: false,
content: {
key: 'content',
otherkey: 'content' // Keep this one
}
}
// returned new state
{
...state,
content: {
key: 'thing I only want to update'
}
}
Actions
export function requestUser() {
return {
type: REQUEST_USER
};
}
export function receiveUser(data) {
return {
type: RECEIVE_USER,
payload: data
};
}
export function fetchUser(userhash) {
return function (dispatch) {
dispatch(requestUser);
return new Promise(resolve => setTimeout(resolve, 500)).then(() => {
const data = {
status: 200,
data: {
firstName: 'Neal',
lastName: 'Van der Valk',
email: 'email#outlook.com',
hash: 'zea7744e47747851',
permissions: {
'admin': true,
'results': true,
'database': true,
'download': true,
'property': true,
'departments': true,
'users': true,
'devices': true,
'integrations': true,
},
notifications: {
'daily': true,
'weekly': false
}
}
};
dispatch(receiveUser(data));
});
};
}
Reducer
const INITIAL_STATE = {
isFetching: true,
isUpdating: false,
content: null
};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case REQUEST_USER:
return {
...state,
isFetching: true
};
case RECEIVE_USER:
return {
...state,
isFetching: false,
content: action.payload.data
};

You can try Object.assign()
This example shows the usage.
{
...state,
content: Object.assign({}, state.content, {
key: 'thing I only want to update'
}
}
You can also use the same spread operator ...
var state = {
isFetching: true,
isUpdating: false,
content: {
key: 'content',
otherkey: 'content' // Keep this one
}
};
var newState = {
...state,
content: {
...state.content,
key: 'thing I only want to update'
}
}
console.log(newState);

In your initial state, content should be {} instead of null.
Then you can change the state in your reducer with Object.assign.
example :
case RECEIVE_USER:
return{
...state,
content: Object.assign({}, state.content, {
key: 'thing I only want to update'
}

Related

Changing attribute in state React.js

I'm doing Todo App in React and I'd use some help. This is probably trivial question, but I don't know how to do it.
context.js:
const initialState = {
isSidebarOpen: true,
isLoading: false,
isEditing: false,
todos: [
{ id: 1608592490852, todo: "Buy milk", important: false },
{ id: 1608592490939, todo: "Take out trash", important: false },
{ id: 1608634291740, todo: "Buy flowers for mom", important: false },
{ id: 1608634291874, todo: "Repair washing machine", important: false },
],
importantTodos: [{ id: 1608634291874, todo: "Repair washing machine" }],};
const handleAction = (e, item) => {
const { id, todo } = item;
const actionName = e.target.getAttribute("name");
if (actionName === actionTypes.addImportant) {
dispatch({ type: actionTypes.addImportant, payload: id });
}
reducer.js:
const reducer = (state, action) => {
const { todos } = state;
switch (action.type) {
case actionTypes.addTodo:
return {
...state,
todos: [...state.todos, { id: action.payload.id, todo: action.payload.todo, important: false }],
};
case actionTypes.addImportant:
const importantTodo = todos.find((item) => item.id === action.payload);
console.log(state);
return {
...state,
todos: [...state.todos, { ...todos, important: true }],
importantTodos: [...state.importantTodos, { id: importantTodo.id, todo: importantTodo.todo }],
};
default:
throw new Error(`No Matching "${action.type}" - action type`);
}};
When adding ToDo to importantTodos, I'd also like to change it's attribute important:false to important: true in todos array. Currently, this code works without changing the attribute when
todos: [...state.todos, { ...todos, important: true }],
line is deleted. With it it just copies all todos, and stores them as new array of objects at the todos array. I think the problem is in my spread operators as I don't understeand them as I tought I do.
Add this snippet in the addImportant case
const updatedTodos = todos.map(todoEl => {
if(todoEl.id === action.payload){
const {id, todo} = todoEl;
return {id, todo, important: true}
}
return todoEl;
})
Update return statement:
return {
...state,
todos: updatedTodos,
importantTodos: [...state.importantTodos, { id: importantTodo.id, todo: importantTodo.todo }],
};

Using es6 shortcuts to reduce number of lines in code snippet

Is there a more concise, prettier way to write this snippet of code? Learning es6 syntactic sugar and was wondering if i can reduce the number of lines for this snippet
const checkLoggedInSuccess = ({
user,
}: {
user?: User;
}): AuthenticationActionTypes => {
if (user === undefined) {
return {
type: CHECK_LOGGED_IN_SUCCESS,
isFetching: false,
};
} else {
return {
type: CHECK_LOGGED_IN_SUCCESS,
isFetching: false,
user: {
email: user.email,
},
};
}
};
how'd'ya fancy this?
const checkLoggedInSuccess = ({user}: {user?: User}): AuthenticationActionTypes => (
!!user
? {
type: CHECK_LOGGED_IN_SUCCESS,
isFetching: false,
}
: {
type: CHECK_LOGGED_IN_SUCCESS,
isFetching: false,
user: {
email: user.email,
},
}
)

how to Modify a child object from you state in the reducer with Redux

I have a state with multiple objects inside it. Right now I can modify all the elements from the first level, like the step property. But I want to know how to modify the elements from an object inside of my state.
Initial state
const initialState = {
step : 1,
user: {
identification : {
email: '',
username: '',
},
localization: {
address: '',
country: '',
}}
My payload is this object:
identification : {
email: "some#html.com"
username: "s032190" }
And my rootReducer is this:
function rootReducer(state = initialState, action) {
switch (action.type) {
case ADD_USER:
return { ...state, user: action.payload }
case CHANGE_STEP:
return { ...state, step: action.payload }
case ADD_FIELD:
Object.keys(state.altaUsuario).forEach (cat => {
return { ...state.user, [cat] : action.payload}
})
default:
return state
}
};
Is it fine to use a loop? I tried with the map or the foreach, nothing works.
I also tried to call property in the spread operator, something like this:
return { ...state, altaUsuario[cat] : action.payload}
But it gives me a syntax error.
I can also modify the payload if is necessary.
Any idea??
You can try something like this
return { ...state,user : {...state.user ,identification :action.payload }}
if your action.payload is
{
identification : {
email: "some#html.com"
username: "s032190"
}
}
you have to override the identification property
return { ...state, user: { ...state.user, identification: action.payload.identification}

Updating a nested object with boolean values

I have a loading object, with boolean values for each loading. My state looks like this:
state: {
loading: {
itemA: false,
itemB: false,
itemC: false
}
}
I want to update my state using setLoading({ itemA: true }) but having this only update itemA while keeping itemB and itemC the same as whatever the current state is.
return {
...state,
loading: {
...state.loading,
itemA,
itemB,
itemC
}
};
Here is the full reducer (condensed):
setLoading: state => ({ itemA, itemB, itemC }) => {
return {
...state,
loading: {
...state.loading,
itemA,
itemB,
itemC
}
};
}
Unfortunately, if I setLoading({ itemC: true }), A and B are now undefined.
How do I ensure they are not undefined, but rather whatever is in the state?
Please note - I have tried passing just anything and doing object assign or spreading loading.
However, to increase readability, I am wondering if I can destructure the props and not have to define each loading (I have like 12 things on this page that load separately - a dashboard).
Thanks
Use Object.assign()
const state = {
loading: {
itemA: false,
itemB: false,
itemC: false
}
}
Object.assign(state.loading, {itemA: true});
console.log(state.loading);
Just pass a variable, that you unfold
setLoading: state => (newLoadingState) => {
return {
...state,
loading: {
...state.loading,
...newLoadingState
}
};
}
You could provide default values in your destructure
const fn = state => ({
itemA = state.loading.itemA,
itemB = state.loading.itemB,
itemC = state.loading.itemC
}) => {
return {
...state,
loading: {
...state.loading,
itemA,
itemB,
itemC
}
};
};
Now when the variables are missing, they're taken from the state passed in;
const fn2 = fn({loading: {itemA: false, itemB: false, itemC: false}});
fn2({itemB: true});
// => {loading: {itemA: false, itemB: true, itemC: false}}

Redux update object with multiple params

Hello i have my reducers like this
case UPDATE_USER: {
return {
...state,
...action.newDataUser,
}
}
My object user is :
{
name: 'azerty',
email: 'azerty#yopmail.com',
birthday: '1990'
}
I have an action to update my user object :
export function updateUser(newDataUser) {
return { type: UPDATE_USER, newDataUser }
}
How to modify my reducers UPDATE_USER to update user object with multiple properties :
{
name: 'azerty updated',
email: 'azerty#yopmail.com updated',
}
Thanks
You can manually update required properties
case UPDATE_USER: {
let user = action.newDataUser;
['name', 'email', 'birthday'].forEach(key => {
user[key] = `${user[key]} updated`;
});
return {
...state,
...user,
}
}

Categories

Resources