Modify selector in redux saga without mutating state - redux

When using a selector, I thought that I could do whatever I wanted with the variable without modifying the state, so I was surprised that the state became mutated.
So if this is wrong (in a redux saga):
const filters = yield select(state => state.filters.filters);
filters.terms['helloo'] = "mutated";
//send data with request
yield put(requestData(filters)
How come that first line is a direct reference to the state?
Anyway, if I try using Object.assign, it also mutates state:
const filters = Object.assign({}, yield select(state => state.filters.filters));
filters.terms['helloo'] = "mutated";
How do I create a selection that is a copy of the state?

There's truly no "magic" involved here. Redux's getState() is literally just return state, and both hand-written selectors and Reselect return whatever you have written the functions to return. So, in that example, filters is the actual object reference that's nested inside the store state, because that's what your function returned.
Per the Redux docs page on "Immutable Update Patterns", you need to copy all levels of nesting that you want to update. In your example, you're making a copy of filters, but not filters.terms, so terms is also still the original object that's in the store. You would need to make a copy of that as well, and modify the copy.

Related

NgRx reducer function with condition

I have a side effect that detects the browser language and dispatches a browserLanguageSupported action if it is a language that my application can handle.
Now I have following reducer function that only updates the states preferredLanguage property in case it is not defined already. This is important because there are other actions that update this state property and I do not want a late browserLanguageSupported action to overwrite such a state update.
export interface State {
preferredLanguage: AppLanguage | undefined;
rehydrationComplete: boolean;
}
export const initialState: State = {
preferredLanguage: undefined,
rehydrationComplete: false
};
export const reducer = createReducer(
initialState,
on(LanguageActions.browserLanguageSupported, (state, {browserLanguage}) => {
if (!state.preferredLanguage) {
return {...state, preferredLanguage: browserLanguage};
}
return state;
})
);
Now for my question: Is it good practice to have such a condition in a reducer operator? The function itself is still pure. But I am not sure if it is good design or if I should solve it differently, lets say by adding state slice selection in the side effect that dispatches this action.
Btw. the reason I am not setting it directly in the initial state is because I get the browser language from an angular service and I am not sure if it is even possible to set initial feature state from service injection?
Best regards,
Pascal
I would to this the same way, so you get a đź‘Ť from me.
Adding a slice of the state into the effect just adds needless complexity.
The reducer contains the state, and it's OK to add logic to see if state needs to be updated or not.
Also, let's say you need to add this logic into another action/effect.
Having it in the reducer makes it easier to reuse if it's needed. Otherwise you end up with duplicate logic.
As long as the rejection (or mutation) of the data is irrelevant to the chain of actions & effects, this is absolutely valid.
However, it's worth noting that if the action in question triggers an effect which triggers an action, the triggered action will not know whether the data was rejected (or mutated) without checking the state—which is exactly what this pattern is attempting to avoid.
So, if you wanted to be able react to that rejection (or mutation), you would want to handle this in the effect. But, if you would proceed in exactly the same manner regardless of the result, then it belongs reducer.

Hot to get state from Redux RTK mutation in another component

Hey i'm new to RTK toolkit and just trying to access state from isolated component to store state from mutation, so i can skip passing them to that component.
I can see the result from mutation in redux store behind the cashe key, but how to access the store data from isolated component?
I read the documentation at least twice and saw that if it's QUERY i can pass useGet** hook with the same args and could get the result. But my case is to access result from mutation (POST query).
Just in case i can create selector to the whole state:
export const selectSubmitStatus = (state) => state[createApi.reducerPath];
and possibly manual filter current item, but it could be easier to access mutation submitState with selector and passing cashe key or?
Simmilar problem here described

Do Redux selectors cause unnecessary component renders?

From what I've understood from Dan Abramov's egghead video 'javascript-redux-colocating-selectors-with-reducers' and some of his tweets, it is a good practice to use a selector to map the state to a prop and remove this logic from the Component and placing it in the Reducer (where the state is managed).
Although this makes all the sense, it also causes my component to render everytime a new state is added to the store, even when only a non related property of the state object was changed. Is there a way to overcome this without using reselectors, which might be a bit overkill for the simpler cases?
As you may know, mapStateToProps is called every time your store is updated.
Whether the component will re-render depends on what mapStateToProps returns. (Actually, it depends on the combined props object returned by mapStateToProps and mapDispatchToProps.)
React Redux (the library that provides the connect function) makes a shallow equality check on the returned object and the last returned object. If the equality check succeeds (i.e. the previously returned object is determined to be equal to the next returned object), the component will not re-render. If the check fails, the component will re-render.
For example, let's say you always return the following object from mapStateToProps:
{
items: [],
}
This object will never be equal to itself ([] === [] returns false because they're different arrays). The equality check will thus fail and the component will re-render.
However, React Redux performs a more complex equality check that that (the implementation of its shallowEqual function can be found here).
For example, even though { a: 'b' } === { a: 'b'} returns false (they're different objects), shallowEqual will pass them off as equal. This is because shallowEqual will compare each key of the returned object with each key of the previously returned object, but only one level deep. More details can be found in the implementation I linked to above.
In summary, if you don't want your component to re-render, you'll need to make sure that the equality check succeeds.
You can:
Save the returned object into the state using a reducer
Cache the result using Reselect
Implement shouldComponentUpdate in the component by hand
These suggestions come straight from Redux's FAQ page: https://redux.js.org/docs/faq/ReactRedux.html#react-rendering-too-often
You can also make sure your mapStateToProps function returns objects that are considered equal by shallowEqual (e.g. objects without arrays, and only one level deep).
For simplicity, I would opt for Reselect.
The short answer is: No it is not.
But there is common mistake that causes unnecessary renders of components when you're using selectors. You should always make sure to define your selector once. So what does it mean?
When you are using connect method, you can pass mapStateToProps method as an argument. Object returned by this method will be passed as props to your component and if you define your selector inside this object it will be redefined each time your component receives a prop. Here is an example for that:
Defining your selector like this could cause your component to render unnecessarily. This is because each time you pass a prop to your component you're basically redefining getSettings method.
#connect(state => ({
getSettings: ()=>'sample output',
}))
class Sample extends React.Component {}
Correct way is the define your selector like this, so that it'll be only created once and the reference passes through your mapStateToProps argument.
const getSettings = () =>'sample output';
#connect(state => ({
getSettings,
}))
class Sample extends React.Component {}

What prevents code from changing the store state?

The store has a method called getState that will return the current state of the store.
What prevents code somewhere in my application from (accidentally) modifying the returned state from store?
Let's say i call this:
let state = store.getState();
state.someProperty = 'fun';
The implementation that i've found on getState on the store object simply returns the inner state object that gets overwritten with each new action.
const getState = () => state;
In between actions/new states what prevents code from modifying the state that will be read by another subscriber? In my above example, setting someProperty to 'fun' will persist inside the store on the state property, until overwritten.
While i'm obviously not supposed to modify the state, a simple mistake might bind the state to some component that (unknowingly) modifies its inputs - perhaps on a 2-way binding in an angular environment?
<app-some-component [user]="state"></app-some-component>
Shouldn't getState() be implemented as a clone of its state model?
P.S. This is not specifically related to Angular - which is why i didn't add the tag - to allow more people not used to Angular to answer the question.
The answer is: nothing :)
The core Redux library itself technically doesn't actually care if state gets mutated or not. You could actually mutate in your reducers, or have other parts of your app get the state tree and mutate it, and the store itself wouldn't know or care.
However, mutation will break time-travel debugging, as well as make tests unreliable. Even more importantly, the React-Redux library assumes that you will handle your state immutably, and relies on shallow equality comparisons to see if the state has changed. (This is the reason why "Why isn't my component re-rendering?" is in the Redux FAQ. 99.9% of the time, it's due to accidental mutation.)
If you are concerned about mutation, you can use a library like Immutable.js instead of plain JS objects, or use one of the several tools for freezing your state in development to catch mutations.

Redux - Use action object method in reducer instead of switch

I'm new to redux and looked at redux-actions or using switch statements in reducer, and though I'm not against using a switch statement, I'm wondering, isn't it easier to just use the call the action method?
Here's what I'm thinking
import actions from './actions'
const reducer = (state = {}, action) => {
if (actions[action.type]) return Object.assign({},
state, actions[action.type](action)
);
return state;
}
I've just tested this on my first reducer and action, and it works, but it seems quite obvious so I'm wondering why the switch type is the chosen way?
Switch statements are certainly the most common approach, but lookup tables are common as well. You can even use plain if/then conditions if you want. Ultimately, how you write your reducers is up to you.
FYI, this topic is covered in the Redux FAQ, in the FAQ: Reducers section. You might also want to read the new "Structuring Reducers" how-to section as well.
Some observations:
Don't refer to these external functions as "actions". They're not actions. They're actually reducers themselves.
Being reducers, you really ought to be passing the state object to them. Oftentimes, you'll want/need to utilise information contained in the current state, as well as information contained in the action object.
Otherwise, this seems like an appropriate approach.

Resources