Limit Redux to update only components affected by the change - reactjs

trying to understand React-Redux, i find it unusual that all my components get new props when ever any slice of the state get changed. so is this by design or i'm doing something wrong ?
example App
class App extends React.Component {
render(){return (
<div>
<Navbar data={this.props.navbar} />
<Content data={this.props.content} />
</div>);
}
}
select (state) => ({ navbar:state.navbar, content:state.content});
export default connect(select)(App);
Components
export const NavbarForm = props => {
console.log('RENDERING with props--->',props);
return (<h1>NAV {props.data.val}</h1>);
};
export const ContentForm = props => {
console.log('RENDERING CONTENT with props--->',props);
return (<h1>CONTENT {props.data.val}</h1>);
};
////////INDEX.js//////
const placeholderReducer = (state={val:0},action)=>{
//will update val to current time if action start with test/;
if(action.type.indexOf('TEST/') === 0)return {val:Date.now();}
return state;
}
export const rootReducer = combineReducers({
navbar:placeholderReducer,
content: (state,action)=>(state || {}), //**this will never do a thing.. so content should never updates right !!**
});
const store = createStore(rootReducer, {}, applyMiddleware(thunk));
render( <Provider store={store}> <App /></Provider>, document.getElementById('app')
);
setInterval(()=>{ store.dispatch(()=>{type:'TEST/BOOM'}) },3000);
okay in this app, what i expect is that Navbar component will get updated every 3000ms while content component will never updates because its reducer will always return the same state.
yet i find it really strange that both components does reRender every time an action is fired.
is this by design ? should i worry about performance if my app has 100+ component ?

This is entirely by design. React assumes that your entire app will be re-rendered from the top down by default, or at least a given subtree will be re-rendered if a certain component does a setState or something similar.
Because you only have the very top component in your app connected, everything from there on down is React's standard behavior. A parent component re-renders, causing all of its children to re-render, causing all of their children to re-render, and so on down.
The core approach to improving UI performance in React is to use the shouldComponentUpdate lifecycle method to check incoming props and return false if the component does not need to re-render. This will cause React to skip re-rendering that component and all of its descendants. Comparisons in shouldComponentUpdate are generally done using shallow reference equality, which is where the "same object references means don't update" thing becomes useful.
When using Redux and connect, you will almost always find yourself using connect on many different components in your UI. This provides a number of benefits. Components can individually extract the pieces of the store state that they need, rather than having to hand them all down from the root component. In addition, connect implements a default shouldComponentUpdate for you, and does a similar check on the values you return from your mapStateToProps function. So, in a sense, using connect on multiple components tends to give you a "free win" in regards to performance.
Further reading on the topic:
Redux FAQ: Connecting multiple components
React/Redux Links: Performance articles

Yes this is by design. Action is dispatched. Reducers run. Store subscribers get notified "the store has changed". Connected components are store subscribers.
Typically you just don't worry about it until you can actually measure a performance problem that you can attribute to this - don't prematurely optimize.
If you find out that it is a problem, then you can do one of the following:
Add a shouldComponentUpdate method to your components so they can see that the props they received aren't different and do not need to render (there are lots of Pure Render mixins & high order components available to make this easy)
Instead of connecting the top-level app, connect the Navbar and Content components directly. The App will never rerender, but the children will if the store changes. And react-redux automatically uses shouldComponentUpdate to only re-render the connected components that actually have new props.

Related

How to keep React new Context API state when routing between Components?

Summary:
1) Do you know how to keep the state of a Context Provider present when it is mounted/unmounted through routing?
2) Or do you know a well maintained Flux implementation that supports multiple separated stores?
In detail:
Besides React components own state I've been using mostly redux so far. Besides not loving the idea of having every state managed globally, even though it might only be relevant for a subtree, it also becomes an issue for my new project.
We want to dynamically load components and add them via routing to the app. To be able to have components ready for plug and play, we want them to take care of their own state (store it, request it from the server, provide a strategy to modify it).
I read about how to dynamically add reducers to the global store with redux, but I actually find the approach of Reacts Context API much nicer where I can encapsulate some state in a Provider and can consume it wherever I need it.
The only issue I have is, that a Provider and a Consumer are React components, so if they are part of a component, that is mounted and unmounted through routing, the state that might have been created or fetched once, is gone.
It seems to me that there is no way to solve that, besides temporarily storing the state in the localstorage or on the server. If there is, please let me know!!!
If there shouldn't be a better solution:
I also thought about a more original Flux implementation that would allow multiple stores, which could be encapsulated with the relavant component subtree. But I haven't really found any well maintained Flux implementation besides Redux. Mobx being the exception, but I really prefer the reducer solution of Redux over the observable solution of Mobx. So again, if you know a multi store Flux implementation that is well maintained, please let me know!!!
I would be really happy about some feedback and hope you can point me into a direction that is more satisfiying than dynamic reducer Redux or temporarily persisted Context state.
Thanks a lot in advance!
Sorry that it's quite a late answer
Are you using React Router?
The state should be persisted and it shouldn't clear if you are navigating correctly. There should be no page reload as this will cause the state to clear.
Here is an example:
import { Router as RootRouter } from 'react-router-dom';
import Router from './routes/Router';
const App = () => {
return (
<MyContext value={useReducer(myReducer, initialState)}>
<RootRouter history={browserHistory}>
<Router />
</RootRouter>
</AuthContext>
);
}
import About from '../components/About';
const Router = () => {
return (
<Switch>
<Route exact path='/about' component={About}></Route>
</Switch>
}
On your main home component, you have to use a Link or Navlink to "switch" between components. Therefore, you'll have something like...
import { Link } from 'react-router-dom';
<Link to="/about">About</Link>
This will navigate you to the about page in which you can still access the context stage where nothing is cleared.
So I figured out a way to work around the problem with Context (first question): I store the state of the Provider component in a variable. That way, when that component is mounted again, it uses the "persisted" state as the initial value for it's state.
let persistedState = {};
const Context = React.createContext();
export class Provider extends React.PureComponent {
state = { ...persistedState };
updateState = (nextState) => this.setState(nextState, () => {
persistedState = {...this.state};
});
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}

Redux mapStateToProps called multiple times

I have this very simple Component, which is connected to redux state and returns {fruit, vegetables}. Everything works fine, but let's say I have a graph inside the Component and I if receive only updated vegetable from API the graph is being recreated each time.
Here's my component:
const Products = ({ fruit, vegetable }) =>
<div className='Products'>
<div>{vegetable.map(....logic here)}</div>
<div>{Math.random()}</div> // this is to illustrate the component is rendering every time
<Graph>Here will be a chart or a graph with fruit</Graph> //but it gets re-rendered even though there is no new fruit
</div>
const mapStateToProps = (state) => {
return {
fruit: state.data.fruit,
vegetable: state.data.vegetable,
}
}
export default connect(mapStateToProps)(Products)
It seems to me that every-time, no matter which states is updated it re-renders the whole components.
Is there a way to prevent that?
When a React component gets rendered, the whole tree of components below it also gets rendered - at the exception of the components which shouldComponentUpdate hook returns false. So in your case, if the Products component gets rendered, it is normal that the Graph component also does.
You have two options here:
if your Products component does not use the fruit prop outside of the Graph component, you can connect directly your Graph component to the fruitstate, and use the pure option of the connect function to avoid re-renders when fruit does not change
you can define the shouldComponentUpdate hook in your Graph component to manually skip unnecessary renders, or use a helper library to do it for you, for example the pure helper of the recompose library
The first option is where optimizing react/redux apps / avoiding unnecessary renders generally starts: connect your components to the store at the lowest level where it makes sense. The second option is more of an escape hatch - but still often useful.
As you mention you use stateless components, you can use a higher-order component to benefit from the shouldComponentUpdate hook. To understand how this works, here's how a simple implementation of it could look like this:
function pure(BaseComponent, shouldUpdateFn) {
return class extends Component {
shouldComponentUpdate(nextProps) {
return shouldUpdateFn(this.props, nextProps);
}
render() {
return <BaseComponent { ...this.props } />;
}
}
}
This would give you a pure HOC that you could reuse over your app to avoid unnecessary renders: it works by wrapping your stateless component into a new component with the desired hook. You'd use it like so, for example:
export default pure(Graph, (props, nextProps) => props.fruit !== nextProps.fruit)
Still, i highly encourage you in having a look at recompose, which has more fine-grained implementations of this, and would avoid you to reinvent the wheel.
To prevent a component to rerender when receiving new props, you can implement shouldcomponentupdate() in Graph.
Use shouldComponentUpdate() to let React know if a component's output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority of cases you should rely on the default behavior.
shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate() is used.
Returning false does not prevent child components from re-rendering when their state changes.
Currently, if shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. Note that in the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.
If you determine a specific component is slow after profiling, you may change it to inherit from React.PureComponent which implements shouldComponentUpdate() with a shallow prop and state comparison. If you are confident you want to write it by hand, you may compare this.props with nextProps and this.state with nextState and return false to tell React the update can be skipped.
To help you implementing shouldComponentUpdate(), you can use eitherReact shallow-compare() or a custom shallow compare function
Given your current code.
React will update the whole component when state is changed.
So Graph Component will get updated.
If you don't want Graph Component to get updated you can add shouldComponentUpdate in your Graph Component and introduce checks there for re-rendering like as follows
shouldComponentUpdate: function(nextProps, nextState) {
// You can access `this.props` and `this.state` here
// and check them against nextProps and nextState respectively.
// return boolean(false) if you don't want the component to re-render.
}

Benefits of redux

I've started learning React without Redux or Flux and have been hearing a lot about Redux and how it seems to be the favourable pattern to use for managing state going forward. My understanding of it is that the entire state of the App lives in the store which I believe is at the top of the React tree. The various child components then 'subscribe' to various states that are relevant to them.
This is somewhat confusing for me as I thought the core structure of React is already setup in this way? Ie if my component has a certain state then to pass it down to its child components in order to use if further down the React tree I would need to add in this.state.example or this.props.example to a component. To me with this approach i'm 'subscribing' the component in a way as well..
Apologies if this is not the right place for questions like this but if someone could tell me what i'm missing here or the added benefit of Redux that would be very helpful!
You are on the right track on the subscribing portion, but what makes Redux wonderful and many other Flux like state management patterns is that you don't have to pass properties down the child chain just so you could update a childs component like so (you could if you wanted to, but not needed):
function Parent() {
return <ChildOne color="red" />
}
function ChildOne(props) {
return <ChildTwo color={props.color} />
}
function ChildTwo(props) {
return <h1>The Color was: {props.color}</h1>
}
It allows you to "dispatch" (a redux/flux term) an action to the state store to update a property on whichever object a component may be subscribed to.
A helpful library for that "connection" is react-redux. It has many capabilities, but the main that I see is connect which is a higher ordered component (HOC) that "wraps" your component with more logic including the part of the redux store that you want to subscribe to.
So the above could be:
export class Parent extends React.Component {
componentDidMount() {
this.props.dispatch(changeColor('red'));
}
render() {
return <ChildOne />
}
}
export default connect((state) => ({ //This property is the redux store
parent: state.parent,
}))(Parent) //higher order component that wraps the component with redux functionality
function ChildOne(){
return (
<ChildTwo />
);
}
export function ChildTwo(props) { //will have childTwo bound in props object
return (
<h1>The Color is: {props.childTwo.color}
);
}
export default connect((state) => ({ //This property is the redux store
childTwo: state.childTwo,
}))
Where the biggest difference is that you didn't have to pass the color from Parent down 2 levels to ChildTwo because it was "subscribed" to the childTwo object within the redux store and you connected that bit of state to the component so any change to the store will trigger the component to rerender from the state change.
The passing of properties and using Redux will make more sense with this medium post of Presentation and Container components, where passing of properties makes sense as you are only going down one child layer deep and the container component is handling logic such as ajax requests, or dispatches to parts of the redux store, etc.

How does a redux connected component know when to re-render?

I'm probably missing something very obvious and would like to clear myself.
Here's my understanding.
In a naive react component, we have states & props. Updating state with setState re-renders the entire component. props are mostly read only and updating them doesn't make sense.
In a react component that subscribes to a redux store, via something like store.subscribe(render), it obviously re-renders for every time store is updated.
react-redux has a helper connect() that injects part of the state tree (that is of interest to the component) and actionCreators as props to the component, usually via something like
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
But with the understanding that a setState is essential for the TodoListComponent to react to redux state tree change(re-render), I can't find any state or setState related code in the TodoList component file. It reads something like this:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
Can someone point me in the right direction as to what I am missing?
P.S I'm following the todo list example bundled with the redux package.
The connect function generates a wrapper component that subscribes to the store. When an action is dispatched, the wrapper component's callback is notified. It then runs your mapState function, and shallow-compares the result object from this time vs the result object from last time (so if you were to rewrite a redux store field with its same value, it would not trigger a re-render). If the results are different, then it passes the results to your "real" component" as props.
Dan Abramov wrote a great simplified version of connect at (connect.js) that illustrates the basic idea, although it doesn't show any of the optimization work. I also have links to a number of articles on Redux performance that discuss some related ideas.
update
React-Redux v6.0.0 made some major internal changes to how connected components receive their data from the store.
As part of that, I wrote a post that explains how the connect API and its internals work, and how they've changed over time:
Idiomatic Redux: The History and Implementation of React-Redux
My answer is a little out of left field. It sheds light on a problem that led me to this post. In my case it seemed the app was Not re-rendering, even though it received new props.
React devs had an answer to this often asked question something to the tune that if the (store) was mutated, 99% of the time that's the reason react won't re-render.
Yet nothing about the other 1%. Mutation was not the case here.
TLDR;
componentWillReceiveProps is how the state can be kept synced with the new props.
Edge Case: Once state updates, then the app does re-render !
It turn out that if your app is using only state to display its elements, props can update, but state won't, so no re-render.
I had state that was dependent on props received from redux store. The data I needed wasn't in the store yet, so I fetched it from componentDidMount, as is proper. I got the props back, when my reducer updated store, because my component is connected via mapStateToProps. But the page didn't render, and state was still full of empty strings.
An example of this is say a user loaded an "edit post" page from a saved url. You have access to the postId from the url, but the info isn't in store yet, so you fetch it. The items on your page are controlled components - so all the data you're displaying is in state.
Using redux, the data was fetched, store was updated, and the component is connected, but the app didn't reflect the changes. On closer look, props were received, but app didn't update. state didn't update.
Well, props will update and propagate, but state won't.
You need to specifically tell state to update.
You can't do this in render(), and componentDidMount already finished it's cycles.
componentWillReceiveProps is where you update state properties that depend on a changed prop value.
Example Usage:
componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}
I must give a shout out to this article that enlightened me on the solution that dozens of other posts, blogs, and repos failed to mention. Anyone else who has had trouble finding an answer to this evidently obscure problem, Here it is:
ReactJs component lifecycle methods — A deep dive
componentWillReceiveProps is where you'll update state to keep in sync with props updates.
Once state updates, then fields depending on state do re-render !
This answer is a summary of Brian Vaughn's article entitled You Probably Don't Need Derived State (June 07, 2018).
Deriving state from props is an anti-pattern in all its forms. Including using the older componentWillReceiveProps and the newer getDerivedStateFromProps.
Instead of deriving state from props, consider the following solutions.
Two best practice recommendations
Recommendation 1. Fully controlled component
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
Recommendation 2. Fully uncontrolled component with a key
// parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
Two alternatives if, for whatever reason, the recommendations don't work for your situation.
Alternative 1: Reset uncontrolled component with an ID prop
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
Alternative 2: Reset uncontrolled component with an instance method
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}
As I know only thing redux does, on change of store's state is calling componentWillRecieveProps if your component was dependent on mutated state and then you should force your component to update
it is like this
1-store State change-2-call(componentWillRecieveProps(()=>{3-component state change}))

Should updating props re-render the entire component?

Let's say I have a CookingClass component that gets initialized like this.
let teachers = makeTeachers(["Amber", "Jason", "Lily"])
let students = makeStudents(["Hopper"])
<CookingClass
teachers={teachers}
students={students}
/>
One of the teachers dropped out:
let newTeachers = makeTeachers(["Amber", "Jason"])
<CookingClass
teachers={newTeachers}
/>
It will make the entire component re-render. I am not sure whether React only calculates the diff and efficiently re-renders it or I must use shouldComponentUpdate to take care of it myself.
More real-world example might be implementing a Google map where there are a million markers and you want to replace one of the markers.
You're changing a so called Virtual DOM node. For every change in a virtual node shouldComponentUpdate() gets called. If you don't implement it yourself it will always return true;
So if you only want to reload your CookingClass in specific cases you would have to implement it yourself.
The pro of React is that it will only re-render Native DOM nodes when they will get changed in the Virtual DOM. This is the "render" which makes React so fast.
Based on your sample code, the component will re-render everytime.
You should use the react-redux (documentation) bindings to "connect" the component to the store.
// ConnectedCookingClass.js
import { connect } from 'react-redux';
import CookingClass from './CookingClass';
const mapStateToProps = (state) => {
return {
teachers: state.teachers,
students: state.students
};
};
const ConnectedCookingClass = connect(mapStateToProps)(CookingClass);
export default ConnectedCookingClass;
Then use this component elsewhere like so:
// OtherComponent.js
import ConnectedCookingClass from './ConnectedCookingClass';
const OtherComponent = React.createElement({
render() {
return (
<div>
<ConnectedCookingClass />
</div>
);
}
});
The react-redux bindings will do some smart things for you, like only re-rendering the component when the props returned by mapStateToProps are actually different than their previous value (via a shallowEqual comparison), so you should try to only return values here, no functions. Functions should be returned in mapDispatchToProps.
The default implementation of shouldComponentUpdate in react-redux will return true when:
ALWAYS if the component is a "pure" component (aka stateless-function)
When the props have been updated manually (after componentWillReceiveProps called)
When the store has changed and the new props are different than the old props.
Here's what that looks like from the source code:
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}
The real DOM Rendering is completely handled by React with very efficient innerHTML inserts and only for changes in the new data structure of your application VirtualDomTree.
shouldComponentUpdate() controls if the component should be recalculated or not. You should use it, when you are rendering statical data, for example. The output of the component will not change, so you could just return false and the first version of the component will be used for ever ;)

Resources