State mutated by getter - javascript

I'm trying to write a getter that returns a simple number from state, decremented by 1
const getters = {
getCurrentView: state => {
return types.PAGES_OBJECT[state.currentViewNum]
},
getCurrentViewNum: state => {
return state.currentViewNum--
}
};
However this actually mutates the state.
I have tried assigning it to a var, but it appears that var becomes a direct reference to state.
How do I do this, without mutating state?

Isn't x-- equivalent to x = x - 1?
The -- operator is mutating your object.
return state.currentViewNum - 1 instead

Related

Redux modifying the a state value without being asked to?

i'm using redux to manage my state , my initial state in the reducer contains to arrays !
the first one(games) is the one that i want to modify , the second one (InitialGames) is Initial one that i don't want it to be modified !
the problem is i only make changes on the first array ! but when i console my state after the logic ! i see that both arrays got changed ??? which is confusing !
The case that i'm going into is PLAYER_DEAD
My Reducer
import { ADD_GAME, PLAYER_DEAD, PUT_INFOS, RESET_GAME } from "./actions";
const initialState = {
games: [],
InitialGames: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case RESET_GAME:
state.games[action.payload.gameIndex] =
state.InitialGames[action.payload.gameIndex];
console.log(state);
return state;
case ADD_GAME:
return {
games: [...state.games, action.payload.game],
InitialGames: [...state.games, action.payload.game],
};
case PUT_INFOS:
return {
gameInfos: action.gameInfos,
};
case PLAYER_DEAD:
let newGames = state.games;
let newInitialGames = state.InitialGames;
console.log("Before Changings", newGames, newInitialGames);
let newTeam = newGames[action.payload.indexGame].teams[
action.payload.index
].players.splice(0, 1);
console.log(
"After changings",
newGames,
newInitialGames
);
return {games:newGames,InitialGames:newInitialGames};
}
return state;
};
This would be occurring because you're passing the same action.payload.game object to both of your arrays here:
return {
games: [...state.games, action.payload.game],
// same objects ---------^------v
InitialGames: [...state.games, action.payload.game],
}
When you access the .teams array in your PLAYER_DEAD case, you're accessing the same array in memory shared by both games and InitialGames, the same goes for anything within that array, including the .players array within your .teams array's objects. Because you're updating your array in place in a non-immutable way by using .splice(), you end up modifying your state directly and thus modifying the same .players array referenced by both games and InitialGames.
You need to ensure that you don't modify your state in place by using methods like .splice(). For your particular case, you would do something like so:
const newGames = state.games.map((game, i) => i === action.payload.indexGame
? game.teams.map((team, j) => j === action.payload.index
? {...team, players: team.players.slice(1)} // note slice, not splice
: team
)
: game
);
Above, we map your arrays, updating the items when the inndx matches the item to update. When updating the players array, we use .slice() to remove the first item from the array.
Writing immutable code isn't always easy, that's why redux toolkit has built-in support for immer when you use an API such as createSlice() API that will allow you to write code like you've been doing the mutates your state. See here for more info.

What exactly is prevState in a functional component's setState?

Returning the (changed) previous state within a setState that one gets from the useState hook doesn't seem to alter state. Run the following straight forward snippet
function App(){
const [state, setState] = React.useState([{x: 0}])
function changeCount(){
setState(prevState => {
console.log('before', prevState[0])
const newState = [...prevState]
newState[0].x += 1 //the shallow copy newState could potentially change state
console.log('after', prevState[0])//here x gets bigger as expected
return prevState //instead of newState we return the changed prevState
})
}
//checking state shows that x remained 0
return <div className='square' onClick={changeCount}>{state[0].x}</div>
}
ReactDOM.render(<App/>, document.getElementById('root'))
.square{
width: 100px;
height: 100px;
background: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>
By clicking the square we trigger setState. Within setState we make a shallow copy newState of the previous state. By changing the copy we change the prevState (maybe unintentionally) as the console confirms. Returning the changed previous state form setState however doesn't change the state as the count remains 0. If we were to return the newState, the behavior would be as expected.
Repeating this, shows that prevState gets bigger, it just doesn't seem to represent the previous state anymore.
Why is that? I made this minimal example on codepen...
Consider that object assignment is just a reference assignment, never a copy
obj1 = {x:42, y:99};
obj2 = obj1; // obj1 and obj2 both reference the same object
obj1.x += 1;
obj2.y += 1;
console.log(obj1.x, obj1.y);
console.log(obj2.x, obj2.y); // prints the same thing since obj1 and obj2 are the same object
In the above example, obj1 is initialized to point to a new object with properties x and y. When obj2=obj1 is made, this is not a copy of obj1 into obj2, but rather obj1 and obj2 now reference the same object.
Hence, when the console.log statements print, they print the same thing because they are both printing property values from the same object.
Similarly, the additional reference to the original object is being made when the shallow copy from prevState to newState occurrs.
obj = {x:42, y:99};
prevState[0] = obj; // prevState[0] is a reference to obj. prevState[0] and obj point to the same exact thing
newState = [...prevState]; // shallow copy, but newState[0] is an object reference. newState[0] and prevState[0] both point to "obj"
newState[0].x += 1; // is actually updating the original object assigned to prevState[0]

How do these 2 notations differ in meaning in React.js?

Came across these 2 notations recently. While both appear to be doing similar things, one of them uses an assignment operator while another one appears to be an object. Are they doing anything different?
const { value = "" } = this.props;
and
let { value: newValue } = newProps;
Both of these are destructing an object. When you use = you are setting a default value if the value is undefined/null. With : you are aliasing the variable. You can even combine them:
const { value : value2 = 'Hello' } = this.props;
console.log(this.props.value); // World
console.log(value2); // World (since value2 IS this.props.value aliased)
// ...or if this.props.value isn't defined
console.log(value2); // Hello
It has nothing to do with React library actually. They are ES6 destructuring assignments.
The first example will create a constant variable called value and will assign whatever is this.props.value (Object, Array, Number). If this.props.value is undefined though it will use default value = "" - empty string.
const { value = "" } = this.props;
In a second example, newValue variable will be created and it will equal to newProps.value (even if it is undefined).
let { value: newValue } = newProps;

reactjs changing object attributes inside the state object

I get an object as response from a get.
Now I assign the object to a state var (object) like this:
this.setState({editData: response.data})
Is there a way to change the values within this object?
I thought about something like this:
this.setState({editData.[var]: [value]})
thanks
Firstly you have to remember that you should never mutate state object directly. So first you shold make a copy of state object and mutate the copy. Then set this copy as state. You can use spread syntax to achieve it:
this.setState((prevState) => ({editData: {...prevState.editData, [var]: value}}));
Here is working example showing that source object is not mutated:
let state = {
editData: {
age: 22
}
};
let age = "age";
let stateCopy = {editData: {...state.editData, [age]: 100}};
console.log(state);
console.log(stateCopy);

Calculate state recursively using map/reduce?

I have an array of objects, let's say
let objs: foo[];
an immutable state object
let state: State;
and a function
transform: (state: State, obj: foo) => State;
So transform calculates a new State from the previous one using the information from the current obj; if you're thinking 'sounds like Redux to me' you could be right.
Is there a way to call transform recursively for each object from the array such, that each calculated state is the input parameter for the next call to transform using map and reduce? I'm only interested in the final state.
I tried something like
let finalState = objs.reduce((prev: State, curr: foo) => transform(prev, curr), state)
but as reduce requires prevand curr to be of type foo (the array type) this is not going to work obviously.
You can use the current index provided by Array.prototype.reduce:
return objs.reduce((prev, next, index) => transform(prev, objs[index]), state);
But this way you're kind off abusing the reduce function. Imo this should and could be done with a regular forEach statement:
function getFinalState(state: State, objs: Obj[]) : State {
objs.forEach(x => state = transform(state, x));
return state;
}
See this TypeScript playground for working samples

Categories

Resources