Exactly when does a component update itself? - reactjs

As far as I know, when a new prop is received, a component is updated based on shouldComponentUpdate function. I have the following container:
I am using redux, and I have the following inside my container.
function filterQuestions(allQuestions, answeredQuestions) {
var filtered = _.reject(allQuestions, (q) => _.contains(answeredQuestions, q.question.id))
return [...filtered]
}
const mapStateToProps = (state) => {
return {
questions: filterQuestions(state.questions.questions, state.questions.answeredQuestions),
}
}
My intention is that the connected component only updates when the output of filterQuestions(...) changes. However, the component updates everytime when there's a change in the global store. If I get rid of filterQuestions and simply pass state.questions, it shows the expected behavior.
I assume there's something wrong with the usage of filterQuestions but I am not sure exactly what is the issue. Can someone pinpoint it for me?

The React Redux package does a shallow equality check whenever it calls your mapStateToProps function, comparing the current returned object against the previous returned object. If the two objects' contents are shallow equal, it doesn't actually re-render your "real" component.
Whenever you use array functions like map() or filter(), you're returning a new array reference. Even if the two arrays have the exact same contents, the objects that have those arrays as fields will no longer be shallow equal, and so React Redux will re-render your component.
There's two main ways to handle this: either use memoized selector functions (such as those provided by the Reselect package) so that you only re-run the filtering when the inputs have actually changed, or implement shouldComponentUpdate on your real component and implement smarter / more complex comparison logic, such as using _.isEqual().
The Redux FAQ also covers this topic: http://redux.js.org/docs/FAQ.html#react-rendering-too-often

Related

setState() hook - setting state variable directly?

I was under the impression that a second "setter" method param was needed for setting variables to state with the useState() hook. For example, in the following line of code, "widgets" would be used to get widgets from local state but setWidgets() would be needed to set widgets to state:
const [widgets, setWidgets] = useState(new Map<string, JSX.Element>());
However, I inadvertently wrote code to set widgets directly to state via the widgets state variable:
widgets.set(...)
I confirmed that this approach is successfully setting widgets to local state. Was the second param required in earlier versions of React hooks and the useState() hook has been simplified to support a single parameter as described above? If so then do you know in which version of React this was introduced? Or am I possibly doing something in my code that is only making me think that the 1-param useState is valid?
Maps, Sets and other complex objects can be updated, but you won't trigger rerenders by calling they're native methods, as the object reference itself would be the same. The overall effect of this becomes evident on these type of state values being passed down through props or context.
The proper way of handling setting state on this type of object is something like this:
setWidgets(prev => {
prev.set(id, value);
return new Map(prev);
})
This ensures that the object reference is updated so that changes are properly propagated.
widgets.set(...) is wrong. It might seem to work depending on how it is used, but the state should always be updated immutably using the second element of the array returned by useState (setWidgets).
You can update the map immutably like this:
setWidgets(prevMap => new Map(prevMap.set(key, value)))
BTW storing JSX as part of the state is bad practice.

What's the idea behind useState functionality in ReactJS?

I am learning ReactJS and I understand the benefits of functional programming. However, I am a little concerned about the actual useState approach. If we assume a variable to be changed, then why declaring it const in the first place?
I see that I can simply use let number = 4; and then render it like this <p>{number}</p>. What I cannot do however is to re-render it automatically just by changing it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so? Is there a specific reason I am missing why it was implemented the way it is? I mean why the developers have decided that if the value needs to be re-rendered upon change, then it must be a const value declared with the help of useState functionality?
I am asking this because I am suspecting I am missing some good points behind this and I would like to understand them.
The variable is declared as a const because you are not allowed to set it directly. In React the state itself is immutable. You are just allowed to change it over setState (or setNumber in your case) or with actions if you use redux.
But why is that? It may seem unnecessary cumbersome in the beginning
First of all, if your variable number changes, react has to trigger a rerender.
If the state is mutable, it requires data-binding because if the number is set, it has to update the view.
In javascript, data-binding works for simple objects, but not well for arrays. Vue.js for example, as an alternative that uses two-way data binding, had a lot of trouble in its early versions when dealing with arrays. That's why there are now only seven predefined methods to interact with arrays (which they added later to solve that problem). Vue Js Array Methods
So a simple reason to declare the state as const is that it works better with arrays. And if you watch the example you gave, setNumber(number + 1) is not that much more to write than number++. But setState(newArray) works, and newArray[i] = newElement would not work, because due to javascript limitations this cannot trigger a rerender.
Secondly, it is a nicer design concept. Think of your component as a function, that returns a view to a state. And if the state changes, you get a new view. It simplifies relationships between properties in your component. If you were allowed to change your state while rendering your component, it would create a mess.
The problem is that you're thinking of a functional component as if it was stateful. But it isn't. It's a function and once you run it, that's it.
Take this example:
function useState() {
let value = 1
function setValue(v) {
value = v
}
return [value, setValue]
}
function myFunction () {
const [value, setValue] = useState(); // <----- we use const here
return null
}
Even though we're using const, the value variable only exists within the function, once the function returns that's it. It's the same for components.
The actual value of value is stored in a whole different scope, where useEffect has access to.
Here's a deep dive on how react works internally if you're interested
React works in render cycles, i.e. some state is declared, the DOM (UI) is computed during the "render phase", and then flushed to the actual DOM during the "commit phase".
Within each cycle state is considered constant (const in JS simply means a variable can't be assigned a new value, you could just as easily declare it with let or var instead and react would work the same) but for react's purpose, state is constant during a render cycle. When it is updated via one of the state update methods, react then kicks off another render cycle (update state, compute diff, commit to DOM) and re-renders when necessary.
This process is important and the reason why external state mutations are considered anti-pattern, it goes against the react workflow and leads to buggy code or worse, UI that doesn't update as expected.
React component lifecycle
I cannot do however is to re-render it automatically just by changing
it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so?
React state updates use a process called reconciliation to figure out what changed and what to re-render. In really simplistic terms, when react state is updated it is updated with a new object reference so a shallow object comparison can more quickly detect that the component state updated.
Declaring state and doing ++number simply changes the value but not the reference, and would be considered a state mutation, an anti-pattern in react.

It's a good pattern to put function in a ReactJS state?

I don't feel right to store a function in a reactjs state, but not sure any disadvantage? Could anyone help analyze it?
view1: function() {
return <div>view1</view>
}
view2: fucntion() {
return <div>view2</view>
}
goView1: function() {
this.setState({contentView: view1})
}
goView2: function() {
this.setState({contentView: view2})
}
render: function () {
return(
{this.state.contentView()}
)
}
There're a few common gotchas about state in react:
Don't mutate state directly, use setState instead
Don't use state when not referenced in render, use instance variable instead.
Well, it seems fine to use state as function, since it's used in your render function to return what to render? Alright, it might not be an anti-pattern, but a better place to put those functions might be in your props, aka render prop, a few reasons:
Think state as your component's data model.
Data are not stale, it changes over time, it could be results from async operations, ui states after user interactions, controlled forms values etc.
Just like redux stores your app's global state, react state should store your component's local state that changes over time, while functions most of time, does nothing more than deferring execution.
Render props is different, it offers IoC (Inversion of Control) for free.
Props most of time similar to local state, should be concerned with UI rendering. However, unlike state owned/managed by component itself, props are passed from parents/clients of the component.
Event handlers for example, event target (usually DOM elements like button, input etc) doesn't need to know how to handle events when creating the component, instead it yields control back to component client/consumer who might need to use setState in its handleEventName handler to update certain container's state shared across children.
Some library (e.g., react router4) also shifted from HOC to render props (e.g., react 16.3 new context API - provider/consumer pair) in favor of its flexibility and explicitness. Instead of composing HOCs before runtime for static version of final enhanced component, you can declare what to render using render props and composing them at runtime in render functions. Thus, you get a clear separation between data (state/props) provider and consumer (where render props are passed from).
Regarding any performance (re-render) concerns, i think it's totally fine to use state as function, since most performance tricks are done by using shouldComponentUpdate hook with shallow compare state/props references (e.g. React.PureComponent, Redux Connect etc), so it's where immutability and memoization comes into play.

How to render component according the object returned by mapstatetoprops? [duplicate]

This question already has answers here:
How to limit react-redux connect update re-renders to specific state branches?
(3 answers)
Closed 5 years ago.
I am new in React.
Scenerio:
Redux State has : X, Y, Z objects:
class A {
render(){...}
}
mapstatetoprops(){
return { X: x1}
}
connect(mapstatetoprops)(A)
I read somewhere, when redux state is changed in X, Y or Z, connect calls mapstatetoprops which rerenders the component A.
However, I want to rerender A only when X is changed. Is this possible ?
I read somewhere, when redux state is changed in X, Y or Z, connect
calls mapstatetoprops which rerenders the component A.
This statement is partially true. A change in Redux state will trigger a call to mapStateToProps() (assuminging it's not a deep-nested state change). However, render() is only called if props or state is modified through setState() (Link).
Therefore, if your Redux state changes, render() will only be called depending on what action you take in mapStateToProps().
I think this is the best source which explain your Problem
Check Official Docs for More information https://redux.js.org/docs/faq/ReactRedux.html
Why isn't my component re-rendering, or my mapStateToProps running?
Accidentally mutating or modifying your state directly is by far the most common reason why components do not re-render after an action has been dispatched. Redux expects that your reducers will update their state “immutably”, which effectively means always making copies of your data, and applying your changes to the copies. If you return the same object from a reducer, Redux assumes that nothing has been changed, even if you made changes to its contents. Similarly, React Redux tries to improve performance by doing shallow equality reference checks on incoming props in shouldComponentUpdate, and if all references are the same, shouldComponentUpdate returns false to skip actually updating your original component.
It's important to remember that whenever you update a nested value, you must also return new copies of anything above it in your state tree. If you have state.a.b.c.d, and you want to make an update to d, you would also need to return new copies of c, b, a, and state. This state tree mutation diagram demonstrates how a change deep in a tree requires changes all the way up.
Note that “updating data immutably” does not mean that you must use Immutable.js, although that is certainly an option. You can do immutable updates to plain JS objects and arrays using several different approaches:
Copying objects using functions like Object.assign() or _.extend(), and array functions such as slice() and concat()
The array spread operator in ES6, and the similar object spread operator that is proposed for a future version of JavaScript
Utility libraries that wrap immutable update logic into simpler functions

Using external objects as state properties in React

Suppose I have some external object inside my JavaScript file:
var data = [{id: 1, name:'Test1'}, {id:2, name: 'Test2'}];
which I pass to ReactDOM:
ReactDOM.render(<Test data={data}/>, document.getElementById('container'));
and as a property to the state object for Test component:
getInitialState: function () {
return {localState: data};
},
Somewhere along the chain, I use this:
handleClick: function () {
data[0].id=55;
this.setState({localState: data});
}
which causes re-render. Full code here: http://jsfiddle.net/44ff2j4b/
Is this a good idea? Basically, having external data which will be modified in place in the component and re-rendered appropriately. Are there some side effects of doing this? As far as I'm aware, it's not OK to modify state in the React component, but the "state" here does not belong to a component...it belongs to the domain logic.
In a React component, "state" is very much supposed to be modified, in-fact state of a component can only be modified within it. However, what you are doing here is seeding the state with a prop and then maintaining it internally. So long as you are not duplicating the prop every-time, merely seeding it, it is ok. Do read more about this here. You should however rename the prop from data to initialData to indicate that it will only be used for seeding the state.
Although it may work, it is not generally a good idea to do this.
React likes you to treat props as immutable. So if you pass data as a prop, you are not supposed to change the original data, like you do in:
data[0].id=55;
If you want to pass data to a react component, copy it as initial state in getInitialState(), and change only the copy inside the react component.
NB: for this, you will need to make a deep copy (so not only copy the array of objects, but also copy the objects themselves). ImmutableJS is a solution/ library often used in conjunction with react.
If you also want to change the original data, the proper way is to pass a function as a prop (in addition to the data prop) to the react component. From the react component, you can then call this function, which can then change the original data. The original data change or update should leave your data-copy inside react component intact.
The code (outside react) can then decide whether or not to re-render the entire react component with the new data (but it is standard practice to call ReactDOM.render() only once).

Resources