Confusion with the principles of Redux - reactjs

I was learning Redux and came across the three principles of it:
single source of truth,
state is read-only,
changes are made with pure functions.
I understood the idea behind the first principle but still find myself in confusion with the other two.
Concerning state is read-only, I got that we can update state only via actions and is it true that the reason for using actions to update state is predictability which ensures that there is no unexpected and sudden changes and thanks to actions it is clear when and how state is changed(action is kinda like protection of state being changed unexpectedly)? Right?
Lastly, about changes are made with pure functions yeah I understood that it also gives predictability but cannot get the actual and clear reason behind that. \
Thus I really need your kind help

Related

Is it always necessary to add all used functions within useEffect to its dependencies array?

I am currently building a Tetris game on React just to practice hooks (used to develop in class components back in the day, kind of left React for a while and yesterday I decided to use it once again).
The game is working perfectly well, and it behaves as expected on each and every situation, however, there is a constant warning related to using a function within useEffect without it being a dependency.
To clarify - I have a useEffect function that all it does is call an updateFunction and is dependent just on the x and y coordinates of the moving Tetris block. The update function updates the state of the board whenever the position of the shape changes.
I know that React re-creates functions on each and every render, but giving a useCallback to the update function would cause it to be re-created endlessly (as then, the complier would ask me to make it dependent on the board state, thus each time it updates the board, it'll be forced to be re-created once again), and this causes an infinite loop of rendering.
Is it really necessary to put every function within useEffect as a dependency, even if said functions only causes a visual rendering to show the current state of the game?
You can always put whole function inside useEffect and you want get this error and problem
Someone just asked the similar question, and my answer is there, React won't let me use `useEffect` in a completely reasonable way
The answer to your question is that, no you don't have to, if the warning doesn't bother you, and your code is still working, then move on.
Otherwise you can try to find a way to disable this linter, or ask yourself why not to meet all the dependency requirement.
It's best to think of the effect dependencies as "correct" or not, and not try to tailor them to achieve some specific behavior. (The vast majority of the time anyway)
This means that if an effect uses a value that could possibly change, then it's declared as a dependency. Sometimes you app works fine if your dependencies are "incorrect" because elsewhere you can guarantee a value is constant. But this is more about maintaining the application than having it work currently.
Later if you change how one of the dependencies works in a refactoring, then the effect now may need to re-run and doesn't leading to strange and hard to diagnose bugs like stale values where you can't tell where they come from.
If this leads to cumbersome hooks with huge dependency arrays that infinite loop themselves, then it's an indication that logic is messy and needs to be refactored, split into multiple effects, rethink how the data flows through your hooks, etc.
It's hard to advise specifically without seeing your code. But in your case it seems like you could make the "update function" take arguments instead, which means it could then be completely static. Or if you did use useCallback that depends on x,y then it would regenerate when those change, but you probably have a lot of logic to run when those change, so that's probably expected.
Summary:
I would advise you to take the warnings of eslint-plugin-react-hooks seriously, and clean your code until it passes. In the long wrong, in a large and complex application, it really helps keep things clean.
There are very rare exceptions that come up when doing non standard things, but it's worth it to try your best on this before resorting to ignoring those warnings.

React Redux anti pattern to calculate and store results in store?

I have at least two approaches to solve a problem.
Repreform calculations inside views on each update (map, filter, find)
Keep an "CurrentState" object inside my redux state.
I choose to create a CurrentState object that stores my calculated result (It calculates the results inside the reducer function).
Approach 2 saves processor calculations, but it feels dirty.
is it considered an antipatern to have a CurrentState object (updated from the reducer) inside the state?
Thank you in advance!
Not totally clear from your question - but I assume that you are talking about situation, that you can compute "result" from other data in your store and asking if storing it (redundantly) in store is good pattern.
Well, generally I would not recommend it as keeping redundant data in store can lead to inconsistent data bugs - if you forgot to keep your data in sync (e.g. when you or someone else working on the project would change one data and forget to change the order place).
The standard patter for this is usually to use selectors and memonization (see https://github.com/reactjs/reselect). The idea is to keep only normalized data in your store but to use selectors in your views. Selectors are responsible to "cache" (memonize) extensive computation. Another advantage of selectors is that it provides level of indirection for your application and you can easily evolve your state (as the project grows) without affecting your whole application. On the other hand for small and one time projects selectors can be just "another" boilerplate - but in general I recommend to use them.
This was a hard anti-pattern.
If you are reading this chances are you don't fully understand redux and I would like to recommend
https://github.com/leoasis/redux-immutable-state-invariant

Is this React app far too simple to use Redux?

While using ReactJS, I have a parent component, which contains two children components:
- Main
- NewReminder
- ExistingReminders
Main passes information to NewReminder as well as ExistingReminders through props. When a NewReminder is created, it must update the list that the user sees of ExistingReminders. This seems like a great use of redux, where the state can be updated from NewReminder, and will update this list, but I don't know if there are alternatives as this might be far too simple to set up an entire redux environment?
Should I go ahead and begin implementing Redux, or do you have any suggestions for alternatives?
It's really quite a broad question, but I'll give it a crack: you should consider taking the advice of the author:
Seriously, it bears repeating.
Local state is fine.
The tradeoff that Redux offers is to add indirection to decouple “what happened” from “how things change”.
Is it always a good thing to do? No. It’s a tradeoff.
Redux doesn't have to be complicated (in fact, you could argue it goes out of its way to stay fairly light and simple). Once you've set up the basics a few times it's not intimidating. However, if you can write a simple application without Redux in a way that's clear and maintainable, sounds like a win to me!

Why we decouple actions and reducers in Flux/Redux architecture?

I've been using Flux first and Redux later for a very long time, and I do like them, and I see their benefits, but one question keeps popping in my mind is:
Why do we decouple actions and reducers and add extra indirections between the call that will express the intent of changing the state (action) and the actual way of changing the state (reducer), in such a way that is more difficult to provide static or runtime guaranties and error checking? Why not just use methods or functions that modify a state?
Methods or function will provide static guaranties (using Typescript or Flow) and runtime guaranties (method/function not found, etc), while an action not handled will raise no errors at all (either static or runtime), you'll just have to see that the expected behavior is not happening.
Let me exemplify it a little better with our Theoretical State Container (TSC):
It's super simple
Think of it as React Component's state interface (setState, this.state), without the rendering part.
So, the only thing you need is to trigger a re-render of your components when the state in our TSC changes and the possibility to change that state, which in our case will be plain methods that modify that state: fetchData , setError, setLoading, etc.
What I see is that the actions and the reducers are a decoupling of the dynamic or static dispatch of code, so instead of calling myStateContainer.doSomethingAndUpdateState(...) you call actions.doSomethingAndUpdateState(...), and you let the whole flux/redux machinery connect that action to the actual modification of the state. This whole thing also brings the necessity of thunks, sagas and other middleware to handle more complex actions, instead of using just regular javascript control flows.
The main problem is that this decoupling requires you to write a lot of stuff just to achieve that decoupling:
- the interface of the action creator functions (arguments)
- action types
- action payloads
- the shape of your state
- how you update your state
Compare this to our theoretical state container (TSC):
- the interface of your methods
- the shape of your state
- how you update your state
So what am I missing here? What are the benefits of this decoupling?
This is very similar to this other question: Redux actions/reducers vs. directly setting state
And let me explain why the most voted answer to that question does not answer either my or the original question:
- Actions/Reducers let you ask the questions Who and How? this can be done with the our TSC, it's just an implementation detail and has nothing to do with actions/reducers themselves.
- Actions/Reducers let you go back in time with your state: again this is a matter of implementation details of the state container and can be achieve with our TSC.
- Etc: state change orders, middleware, and anything that is currently achieved with actions/reducers can be achieved with our TSC, it's just a matter of the implementation of it.
Thanks a lot!
Fran
One of the main reasons is that constraining state changes to be done via actions allows you to treat all state changes as depending only on the action and previous state, which simplifies thinking about what is going on in each action. The architecture "traps" any kind of interaction with the "real world" into the action creator functions. Therefore, state changes can be treated as transactions.
In your Theoretical State Container, state changes can happen unpredictably at any time and activate all kinds of side effects, which would make them much harder to reason about, and bugs much harder to find. The Flux architecture forces state changes to be treated as a stream of discrete transactions.
Another reason is to constrain the data flow in the code to happen in only one direction. If we allow arbitrary unconstrained state modifications, we might get state changes causing more state changes causing more state changes... This is why it is an anti-pattern to dispatch actions in a reducer. We want to know where each action is coming from instead of creating cascades of actions.
Flux was created to solve a problem at Facebook: When some interface code was triggered, that could lead to a cascade of nearly unpredictable side-effects each causing each other. The Flux architecture makes this impossible by making every state transition a transaction and data flow one-directional.
But if the boilerplate needed in order to do this bothers you, you might be happy to know that your "Theoretical State Container" more or less exists, although it's a bit more complicated than your example. It's called MobX.
By the way, I think you're being a bit too optimistic with the whole "it's an implementation detail" thing. I think if you tried to actually implement time-travel debugging for your Theoretical State Container, what you would end up with would actually be pretty similar to Redux.

Redux: Why is avoiding mutations such a fundamental part of using it?

I'm new to Redux - and I'm really trying to get the big picture of using functional programming to make unidirectional data more elegant.
The way I see it- each reducer is taking the old state, creating a new state without mutating the old state and then passing off the new state to the next reducer to do the same.
I get that not causing side effects helps us get the benefits of a unidirectional flow of data.
I just really don't get what is so important about not mutating the old state.
The only thing I can think of is maybe the "Time-Traveling" I've read about because, if you held on to every state, you could perform and "undo".
Question:
Are there other reasons why we don't want to mutate the old state at each step?
Working with immutable data structures can have a positive impact on performance, if done right. In the case of React, performance often is about avoiding unnecessary re-rendering of your app, if the data did not change.
To achieve that, you need to compare the next state of your app with the current state. If the states differ: re-render. Otherwise don't.
To compare states, you need to compare the objects in the state for equality. In plain old JavaScript objects, you would need to deep compare in order to see if any property inside the objects changed.
With immutable objects, you don't need that.
immutableObject1 === immutableObject2
basically does the trick. Or if you are using a lib like Immutable.js Immutable.is(obj1, obj2).
In terms of react, you could use it for the shouldComponentUpdate method, like the popular PureRenderMixin does.
shouldComponentUpdate(nextProps, nextState) {
return nextState !== this.state;
}
This function prevents re-rendering, when the state did not change.
I hope, that contributes to the reasoning behind immutable objects.
The key of the "no-mutations" mantra is that if you can not mutate the object, you are forced to create a new one (with the properties of the original object plus the new ones).
To update the components when an action is dispatched, Redux connector checks if the object is different, not if the properties have changed (which is a lot faster), so:
If you create a new object, Redux will see that the object is not the same, so it will trigger the components updates.
If you mutate the objet that it is already in the store (adding or changing a property, for example) Redux will not see the change, so it will not update the components.
I'm pretty new to Redux (and React.js) too, but this is what I understand from learning this stuff.
There are several reasons why immutable state is chosen over the mutable one.
First of all, mutation tracking is pretty difficult. For example when you are using a variable in several pieces of code and the variable can be modified in each of this places, you need to handle each change and synchronize results of mutation.
This aproach in many cases leads to bidirectional data flows. Pieces of data are flowing up and down across the functions, variables and so on. Code starts beeing polluted by if-else constructions that are oly responsible for handling state changes.
When you add some asynchronous calls your state changes can be even harder to track.
Of course we can subscribe to data events (for example Object.observe), but it can lead to situation that some part of application that missed change stays out of sync with other part of your program.
Immutable state helps you to implement unidirectional data flow that helps you to handle all changes. First of all data flows from top to bottom. That means all changes that were applied to the main model are pushed to the lower components. You can always be sure that the state is the same in all places of the application, because it can be changed only from one place in the code - reducers.
There is also one thing worth of mentioning - you can reuse data in several components. State cannot be changed (a new one can be created), so it's pretty safe to use same piece of data in several places.
You can find more information about pros and cons of mutability (and about the reason why it was chosen as a main approach of Redux) here:
Pros and Cons of using immutability with React.js
React.js Conf 2015 - Immutable Data and React
Immutable Data Structures and JavaScript
Introduction to Immutable.js and Functional Programming Concepts
Why immutable collections?
Redux checks if the old object is the same as the new object by comparing the memory locations of the two objects. If you mutate the old object’s property inside a reducer, the “new state” and the “old state” will both point to the same object and Redux will infer that nothing has changed.
No reasons. The are no any fundamental reasons that shouldComponentUpdate "pure render" optimization can't work with mutable state containers. This library does it, for instance.
https://github.com/Volicon/NestedReact
With immutable data the reference to the data structure itself can be used as version token. Thus, comparing the references you're comparing the versions.
With mutable data you will need to introduce (and compare) separate version tokens, which is hard to do manually but can easily be achieved with smart "observable" objects.
There are several reasons.
Because of the history (undo/redo) feature.
You can use the history feature also for debugging.
Race conditions: Lets say you have a service that logs some state data
after 1s. If you change the state before the service has logged the
data the service will log the wrong data. Of course you could copy
the state before passing it to the service, but it's easy to enforce
this rule if you do it in a mutation/method in one place, the store, the
single source of truth.

Resources