What exactly does it mean when we say we cannot mutate the state object in Redux reducer? - redux

Does it exactly mean treating it as "read-only"?
For example, if the state is:
{
arr: arrData,
obj: objData,
aMap: mapData,
aSet: setData
}
For any of the four that has no change to it, we can keep it as is, but let's say 3 level down obj, which is obj.b.c.d, and this d refers to an array, and we need to modify one entry in the array, then we need to make a copy of that array, modify the entry, and let the new state refer to it? But if d refers to a new array, then c needs to have a new d, so c needs to be a new object, and for the same reason, we need a new b, eventually, a new obj?
So it is
let objNew = {
...obj,
b: {
...obj.b,
c: {
...obj.b.c,
d: [...obj.b.c.d]
}
}
};
objNew.b.c.d[someIndex] = someNewValueOrObj;
And now objNew can be returned and the old state was not modified in any way (was reading it only).
So if prevState was the old state, and state is the new state, we just need to make sure right before we return the new state, for prevState, if we were to dump all the values out, it stayed the same as what was passed in, while the new state would dump out the values for our new state.

Redux uses shallow equality to check whether the state has changed to trigger a render in React. If the references to the new state and the previous are the same, i.e. === returns true, Redux considers nothing has changed and simply returns from the reducer.
You will have to produce a new state value that is referentially different to the old one no matter how deep down in your state a value has changed.
I would suggest you consider using a library like immer to help you with that in an efficient way.

Related

update state in ngrx reducer immutablely

I'm becoming a bit confused about how to update state in NgRx reducer, here is the question.
Say I have a state
{
xxx: num
yyy: classtpe
...
data: Data[]
}
If I have an action to add a Data item to the list.
I know I can't call data.push() because that just update the array but data pointing to same array so in the reducer I have
state.data = cloneDeep(state.data)
state.data.push(newdata)
so state.data now is different from previous one because they are 2 individual arrays.
my question is, can I return state directly now? If yes then the old and new states point to same variable, they have exactally same members except data.
The other way is, return a brand new state like
return Object.assign({}, state, {
data: [...state.data, newdata]
})
or
const newstate = cloneDeep(state)
newstate.data.push(new data)
return newstate
this way new and old state are totally different.
I think it's actually related to how difference is checked in NgRx? first way they need to go through each memeber, if any member is different then the state is differnet?
2nd way the 2 states are different, but all the memebers need to be checked to see if contents are same.
I would recommend you to use ngrx-immer, https://github.com/timdeschryver/ngrx-immer
It's an Immer wrapper around ngrx reducers so you can update all of the state mutably, and it will do the rest for you. Immer is also used in the redux-toolkit to make it simple to update state.

Working with an LRU and redux store strategy

I wanted to implement an LRU for a react-redux application, however I'm not sure what the best strategy of reading and writing data to the store via reducer so that I can maintain the LRU structure.
The goal is to implement an LRU for a most recent list of users. Effectively, whenever the application click on a specific contact, they get added to the most recent list of users. Let's say the list max out at 10 users, so effectively when it hit the max i'll pop off the oldest access user on the list.
I could associate a timestamp for each user in the list, but that means every time I read the state from the store, I would have to sort and find the oldest time stamp which i feel is slow.
I'm new to React/Redux, so please bear with me.
Any suggestions appreciated!
Thanks,
Derek
I would just have a seperate reducer that acts on the "select contact" action (there is probably another reducer that will also act on to set the currently selected user). It will maintain the array and just push to the front, and if the max is reachers, pop off the end.
Something like:
const initialState = []
export const lruReducer = (state = initialState, action) => {
switch(action.type) {
case 'SELECT_CONTACT':
// copy the previous array (I'm assuming ES6 syntax here, but you could use Object.assign or ImmutableJS or something if preferred)
// this is important to keep the state immutable
let newState = [...state]
// add the new contact (this is where you would do any de-duping logic
newState.unshift(action.user)
// keep removing items until constraint is met
while (newState.length > 10) {
newState.pop()
}
// return new array
return newState
default:
return state
}
}
Then just combine this with your other reducers like normal.

Keeping big requests in redux state

I use immutable.js to keep my application state. It's made of multiple combined reducers, that are immutable maps like:
const initialState = new Map({
data: null,
page: 1,
...
});
So I have a request response I need to fetch, save somewhere, get data from, and update the state, whenever something on the webpage changes.
The question is - where do I keep raw request responses in redux?
Let's say, I want to keep it inside the state. I tried the following approaches:
case FETCH_RESPONSE:
return state.merge({
rawRequestResponse: action.response // very slow, deep conversion to Immutable
})
case FETCH_RESPONSE:
return state.set({
rawRequestResponse: action.response // also slow, it's converted to Immutable, though not deeply
})
case FETCH_RESPONSE:
return new Map({
rawRequestResponse: action.response, // just like above
...
})
But they all are too slow. What's the best way to do this?
Not sure about passing a plain object to set(), but if you pass the key as a string, then the value as the second argument, nothing should be converted:
return state.set('rawRequestResponse', action.response)

Redux with Immutable JS

When using immutablejs with Redux, we will get a regular javascript object back from combineReducers, meaning it won't be an immutable data structure even if everything within it is. Doesn't this mean that using immutablejs will be in vain since a whole new state object will be created on every action anyhow?
Example:
const firstReducer = (state = Immutable.Map({greeting : 'Hey!'})) => {
return state
}
const secondReducer = (state = Immutable.Map({foo : 'bar'})) => {
return state
}
const rootReducer = combineReducers({
firstReducer, secondReducer
})
a whole new state object will be created on every action
Yes but the slices of that state that are assigned by combineReducers are not recreated. It's sort of analogous to doing this:
const person = { name: 'Rob' };
const prevState = { person };
const nextState = { person };
I created a new state object (nextState), but its person key is still set to the same exact object as was prevState's person key. There is only one instance of the string 'Rob' in memory.
The problem is when I mutate the person object, I'm changing it for multiple states:
const person = { name: 'Rob' };
const prevState = { person };
person.name = 'Dan'; // mutation
const nextState = { person };
console.log(prevState.person.name); // 'Dan'
Coming back to Redux, once all reducers have been called for the first time, they will have initialized their slices of the application's state, and your application's entire overall state would basically be equal to this:
{
firstReducer: Immutable.Map({greeting : 'Hey!'}),
secondReducer: Immutable.Map({foo : 'bar'}),
}
Note that this is a normal object. It has properties that hold Immutable objects.
When an action is dispatched and goes through each reducer, the way you've got it, the reducer simply returns the existing Immutable object back again, it doesn't create a new one. Then the new state is set to an object with a property firstReducer simply pointing back to the same Immutable object that the previous state was pointing to.
Now, what if we didn't use Immutable for firstReducer:
const firstReducer = (state = {greeting : 'Hey!'}) => {
return state
}
Same idea, that object that is used as the default for state when the reducer is first called is just passed from the previous state to the next state. There is only ever one object in memory that has the key greeting and value Hey!. There are many state objects, but they simply have a key firstReducer that points to the same object.
This is why we need to make sure we don't accidentally mutate it, but rather replace it when we change anything about it. You can accomplish this without Immutable of course by just being careful, but using Immutable makes it more foolproof. Without Immutable, it's possible to screw up and do this:
const firstReducer = (state = {greeting : 'Hey!'}, action) => {
switch (action.type) {
case 'CAPITALIZE_GREETING': {
const capitalized = state.greeting.toUpperCase();
state.greeting = capitalized; // BAD!!!
return state;
}
default: {
return state;
}
}
}
The proper way would be to create a new state slice:
const firstReducer = (state = {greeting : 'Hey!'}, action) => {
switch (action.type) {
case 'CAPITALIZE_GREETING': {
const capitalized = state.greeting.toUpperCase();
const nextState = Object.assign({}, state, {
greeting: capitalized,
};
return nextState;
}
default: {
return state;
}
}
}
The other benefit Immutable gives us is that if our reducer's slice of the state happened to have a lot of other data besides just greeting, Immutable can potentially do some optimizations under the hood so that it doesn't have to recreate every piece of data if all we did was change a single value, and yet still ensure immutability at the same time. That's useful because it can help cut down on the amount of stuff being put in memory every time an action is dispatched.
combineReducers() that comes with Redux indeed gives you a plain root object.
This isn’t a problem per se—you can mix plain objects with Immutable objects as long as you don’t mutate them. So you can live with this just fine.
You may also use an alternative combineReducers() that returns an Immutable Map instead. This is completely up to you and doesn’t make any big difference except that it lets you use Immutable everywhere.
Don’t forget that combineReducers() is actually easy to implement on your own.
Doesn't this mean that using immutablejs will be in vain since a whole new state object will be created on every action anyhow?
I’m not sure what you mean by “in vain”. Immutable doesn’t magically prevent objects from being created. When you set() in an Immutable Map, you still create a new object, just (in some cases) more efficiently. This doesn’t make a difference for the case when you have two keys in it anyway.
So not using Immutable here doesn’t really have any downsides besides your app being slightly less consistent in its choice of data structures.

What is the purpose of the state monad?

I am a JavaScript developer on a journey to up my skills in functional programming. I recently ran into a wall when it comes to managing state. When searching for a solution I stumbeled over the state monad in various articles and videos but I have a really hard time understanding it. I am wondering if it is because I expect it to be something it is not.
The problem I am trying to solve
In a web client I am fetching resources from the back end. To avoid unnecessary traffic I am creating a simple cache on the client side which contains the already fetched data. The cache is my state. I want several of my modules to be able to hold a reference to the cache and query it for its current state, a state that may have been modified by another module.
This is of course not a problem in javascript since it is possible to mutate state but I would like to learn more about functional programming and I was hoping that the state monad would help me.
What I would expect
I had assume that I could do something like this:
var state = State.of(1);
map(add(1), state);
state.evalState() // => 2
This obviously doesn't work. The state is always 1.
My question
Are my assumptions about the state monad wrong, or am I simply using it incorrectly?
I realize that I can do this:
var state = State.of(1);
var newState = map(add(1), state);
... and newState will be a state of 2. But here I don't really see the use of the state monad since I will have to create a new instance in order for the value to change. This to me seems to be what is always done in functional programming where values are immutable.
The purpose of the state monad is to hide the passing of state between functions.
Let's take an example:
The methods A and B need to use some state and mutate it, and B needs to use the state that A mutated. In a functional language with immutable data, this is impossible.
What is done instead is this: an initial state is passed to A, along with the arguments it needs, and A returns a result and a "modified" state -- really a new value, since the original wasn't changed. This "new" state (and possibly the result too) is passed into B with its required arguments, and B returns its result and a state that it (may have) modified.
Passing this state around explicitly is a PITA, so the State monad hides this under its monadic covers, allowing methods which need to access the state to get at it through get and set monadic methods.
To use the stateful computations A and B, we combine them together into a conglomerate stateful computation and give that conglomerate a beginning state (and arguments) to run with, and it returns a final "modified" state and result (after running things through A, B, and whatever else it was composed of).
From what you're describing it seems to me like you're looking for something more along the lines of the actor model of concurrency, where state is managed in an actor and the rest of the code interfaces with it through that, retrieving (a non-mutable version of) it or telling it to be modified via messages. In immutable languages (like Erlang), actors block waiting for a message, then process one when it comes in, then loop via (tail) recursion; they pass any modified state to the recursive call, and this is how the state gets "modified".
As you say, though, since you're using JavaScript it's not much of an issue.
I'm trying to answer your question from the perspective of a Javascript developer, because I believe that this is the cause of your problem. Maybe you can specify the term Javascript in the headline and in the tags.
Transferring of concepts from Haskell to Javascript is basically a good thing, because Haskell is a very mature, purely functional language. It can, however, lead to confusion, as in the case of the state monad.
The maybe monad for instance can be easily understood, because it deals with a problem that both languages are facing: Computations that might go wrong by not returning a value (null/undefined in Javascript). Maybe saves developers from scattering null checks throughout their code.
In the case of the state monad the situation is a little different. In Haskell, the state monad is required in order to compose functions, which share changeable state, without having to pass this state around. State is one or more variables that are not among the arguments of the functions involved. In Javascript you can just do the following:
var stack = {
store: [],
push: function push(element) { this.store.push(element); return this; },
pop: function pop() { return this.store.pop(); }
}
console.log(stack.push(1).push(2).push(3).pop()); // 3 (return value of stateful computation)
console.log(stack.store); // [1, 2] (mutated, global state)
This is the desired stateful computation and store does not have to be passed around from method to method. At first sight there is no reason to use the state monad in Javascript. But since store is publicly accessible, push and pop mutate global state. Mutating global state is a bad idea. This problem can be solved in several ways, one of which is precisely the state monad.
The following simplified example implements a stack as state monad:
function chain(mv, mf) {
return function (state) {
var r = mv(state);
return mf(r.value)(r.state);
};
}
function of(x) {
return function (state) {
return {value: x, state: state};
};
}
function push(element) {
return function (stack) {
return of(null)(stack.concat([element]));
};
}
function pop() {
return function (stack) {
return of(stack[stack.length - 1])(stack.slice(0, -1));
};
}
function runStack(seq, stack) { return seq(stack); }
function evalStack(seq, stack) { return seq(stack).value; }
function execStack(seq, stack) { return seq(stack).state; }
function add(x, y) { return x + y; }
// stateful computation is not completely evaluated (lazy evaluation)
// no state variables are passed around
var computation = chain(pop(), function (x) {
if (x < 4) {
return chain(push(4), function () {
return chain(push(5), function () {
return chain(pop(), function (y) {
return of(add(x, y));
});
});
});
} else {
return chain(pop(), function (y) {
return of(add(x, y));
});
}
});
var stack1 = [1, 2, 3],
stack2 = [1, 4, 5];
console.log(runStack(computation, stack1)); // Object {value: 8, state: Array[3]}
console.log(runStack(computation, stack2)); // Object {value: 9, state: Array[1]}
// the return values of the stateful computations
console.log(evalStack(computation, stack1)); // 8
console.log(evalStack(computation, stack2)); // 9
// the shared state within the computation has changed
console.log(execStack(computation, stack1)); // [1, 2, 4]
console.log(execStack(computation, stack2)); // [1]
// no globale state has changed
cosole.log(stack1); // [1, 2, 3]
cosole.log(stack2); // [1, 4, 5]
The nested function calls could be avoided. I've omitted this feature for simplicity.
There is no issue in Javascript that can be solved solely with the state monad. And it is much harder to understand something as generalized as the state monad, that solves a seemingly non-existing problem in the used language. Its use is merely a matter of personal preference.
It indeed works like your second description where a new immutable state is returned. It isn't particularly useful if you call it like this, however. Where it comes in handy is if you have a bunch of functions you want to call, each taking the state returned from the previous step and returning a new state and possibly another value.
Making it a monad basically allows you to specify a list of just the function names to be executed, rather than repeating the newState = f(initialState); newNewState = g(newState); finalState = h(newNewState); over and over. Haskell has a built-in notation called do-notation to do precisely this. How you accomplish it in JavaScript depends on what functional library you're using, but in its simplest form (without any binding of intermediate results) it might look something like finalState = do([f,g,h], initialState).
In other words, the state monad doesn't magically make immutability look like mutability, but it can simplify the tracking of intermediate states in certain circumstances.
State is present everywhere. In class, it could be the value of its properties. In programs it could be the value of variables. In languages like javascript and even java which allow mutability, we pass the state as arguments to the mutating function. However, in languages such as Haskell and Scala, which do not like mutation(called as side-effects or impure), the new State (with the updates) is explicitly returned which is then passed to its consumers. In order to hide this explicit state passes and returns, Haskell(and Scala) had this concept of State Monad. I have written an article on the same at https://lakshmirajagopalan.github.io/state-monad-in-scala/

Resources