I'm trying to use some kind of memoization in my workflow with React, and I'm searching for the best and most importantly the "easiest" solution to integrate with my workflow that includes React and Redux.
I came across many articles talking about memoization in general and some demonstrate the use of "memoize-one" and brace it as the fastest and easiest to get up and running with, and others don't even mention it and talk about "reselect".
I just want to know which is better and easiest and which should I invest in.
Both libraries return a function which accepts a given numbers of arguments and returns a value:
getA(arg1, arg2, arg3) // Returns a value
The difference lays in what happens under the hoods when the function is called.
memoize-one
collect provided arguments
compare arguments with the ones provided in previous call (===)
arguments are the same: return cached result
arguments are NOT the same: re-evaluate result function and return
reselect
collect provided arguments
run a set of inputSelectors function providing them with the collected arguments
collect inputSelectors return values
compare inputSelectors return values with the ones obtained in previous call (===)
values are the same: return cached result
values are NOT the same: re-evaluate result function and return
Conclusions
memoize-one is a value-based memoize utility: memoization is performed over the value of provided arguments.
reselect adds a further evaluation layer on top of it: memoization is NOT performed over arguments values BUT over the results of a set inputSelectors functions fed with those initial arguments.
It means that reselect selectors are easily composable since each inputSelectors can be another reselect selector.
I haven't used reselect, but memoize-one works great for me when I want to calculate something from props inside render. This is a great pattern for doing an expensive operation, like mapping a large array, on props that may change over time but also may not change on some re-renders. It ensures an expensive operation used in render is re-computed only when the inputs change. It also avoids having to use lifecycle methods like getDerivedStateFromProps (if it can be calculated from props, it probably shouldn't be on state).
import memoize from 'memoize-one'
class Example extends Component {
mapList = memoize(
(list) => list.map(item => ({text: item.text}))
)
render() {
// if this.props.list hasn't changed since the last render
// memoize-one will re-use the last return value from cache
const mappedList = this.mapList(this.props.list)
return (
...
)
}
}
Keep in mind, in most cases, you’ll want to attach the memoized function to a component instance vs. using a static class variable. This prevents multiple instances of a component from resetting each other’s memoized keys.
react memoization reactjs
I suggest to use reselect, since it was specifically designed to use with React/Redux. memoize-one is more like a general purpose memoization library.
It's really easy to use reselect, it just wraps your selectors:
import { createSelector } from 'reselect';
const shopItemsSelector = state => state.shop.items;
// without reselect
const subtotalSelector = state => {
const items = shopItemsSelector(state);
return items.reduce((acc, item) => acc + item.value, 0);
}
// with reselect
const subtotalSelector = createSelector(
shopItemsSelector, // list the selectors you need
items => items.reduce((acc, item) => acc + item.value, 0) // the last argument is actual selector
)
Related
I am learning Redux Toolkit. From a React POV it seems very intuitive to access whatever part of the state you need from within useSelector using an inline arrow function, and then conduct any calculations. As an example consider a cart item with its data (like item count) in redux store.
function CartItemCounter({ itemId }){
const cart = useSelector(state => state.cart);
const itemInCart = cart.items[itemId];
const count = itemInCart?.count || 0;
return <div>{itemId} - {count} nos</div>
}
But I'm seeing all this information saying you should define your selectors beside your slice, use createSelector and all. What is the right way, and why is it better?
The information that is out there is essentially talking about different levels of optimization while using useSelector.
What you need to understand before anything else, is how useSelector works internally.
How does useSelector work?
When you pass a function to useSelector (obviously inside a react component), it essentially hooks on to the global redux state. Whenever any change happens in any part of the global state (i.e. when dispatch() is called from any part of the app), redux will run all the functions you passed to useSelector in your app, and perform certain checks.
Redux will take the result from each function, and compare it to the value it got the last time it ran the same function.
How does it make this comparison?
It uses a reference equality for this comparison. So if redux has to think that the result of the function hasn't changed, either the value returned from the function has to be a primitive and equal.
4 === 4 // true
'itemA' === 'itemA // true
Or, the value returned must be a derived data type (arrays, objects), with the same reference. So essentially the same object.
const x = { name: 'Shashi' }
const fn1 = () => x;
const fn2 = () => x;
const fn3 = () => { name: 'Shashi' }
fn1() === fn2(); // true
fn1() === fn3(); // false, because the objects are different, with different references
In practice, redux changes the wrapping object if either a key (or key of a nested object) is changed, or you manually change the entire object using a dispatch action (This is related to the immer library integration). This is similar to how you would do in regular React.
/* See how most keys are spread in, and will hence maintain reference equality.
While certain keys like 'first', 'first.second', 'first.second[action.someId]'
are changed with new objects, and so will break reference equality */
function handwrittenReducer(state, action) {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue,
},
},
},
}
}
Otherwise it maintains the same objects within its state, and returns the exact same objects with the same references, when you access them. To verify this, if you access your cart twice, its literally going to be the same object.
const cart1 = useSelector(state => state.cart)
const cart2 = useSelector(state => state.cart)
cart1 === cart2; // true
What does it do with this comparison?
If the comparison returns true, i.e. the new value is the same as the old value, Redux tells that instance of useSelector to chill tf out, and not do anything. If it returns false however, it tells that component to re-render. After all, the value you are accessing from state has "changed"(according to Redux laws), so you probably want to show the new value.
With this information, we can make change the kind of function we pass to the useSelector, in order to get certain optimization benefits.
Optimization Levels
Level 0: Accessing slice data inline
const cart = useSelector(state => state.cart)
// extract the information you need from within the cart
const itemInCart = cart.items[itemId];
const count = itemInCart?.count || 0;
This is not a good way to access the data. You actually need a subset of the data from the cart, but you are fetching the whole thing, and doing the calculation outside the selector.
Problems:
When you put stuff like this inline, what happens if you change the shape of your data in the future? You have to go to every place that uses useSelector and manually change it. Not so good.
More importantly, every time any part of the cart changes, the entire cart object actually changes. So Redux sees your component that asks for the cart, and thinks
The cart has changed. This component is asking for the cart. It should probably re-render.
BAM Every single instance of this component rerenders. And for what? The count of the item you're referencing probably didn't change. So ideally there shouldn't have been a re-render.
Level 1: Centralize the selector
An easy optimization is to put the selector function in a centralized location next to your slice. That way, if your data shape changes in the future, you can just change it in one place, and your whole app (wherever it uses that data) will work with the new shape.
// inside or next to the slice file
const selectCart = (state) => state.cart
//...
// somewhere inside a react component
const cart = useSelector(selectCart)
Level 2: Access the relevant data
Since redux is comparing the results of your selector function, if you want to avoid unnecessary rerenders, you want to make sure the results have reference equality (===). So target the exact value you wish to look at, in your selector.
// extract the information you need from within the cart, *within the selector*
const count = useSelector(state => state.cart.items[itemId]?.count || 0)
// You don't have to use a one-liner, a multi-line function is better for readability
When Redux executes these functions, it keeps a record of the value returned from these selector functions, for each individual useSelector. This time the values are going to be the same for every single counter, except the one that actually changed. All those other counters that didn't actually change in value don't have to unnecessarily re-render anymore!
And if any of you folks think this is premature optimization, the answer's no. This is more along the lines of putting a dependancy array on your useEffects to avoid infinite loops.
Not forgetting the Level 1 optimization, we can also extract this function centrally
const selectItemById = (state, itemId) => (state.cart.items[itemId]?.count || 0);
function CartItemCounter({ itemId }){
//...
// somewhere inside a react component
const count = useSelector((state) => selectCart(state, itemId))
//...
}
So that solves all of our problems right?
For now, yes. But what if this selector function has to run some expensive computation.
const selectSomething = (state) => reallyExpensiveFn(state.cart)
//...
// somewhere inside a react component
const cart = useSelector(selectSomething)
You don't want to keep running that do you?
Or what if you have no option but to return new objects from your select function. A common scenario for this case would be returning a subset of data from the state.
const selectFilteredItems = (state) => state.itemsArray.filter(checkCondition) // the filter method will always return a new array
//...
// somewhere inside a react component
const cart = useSelector(selectFilteredItems) // re-renders every time
To solve this you would have to memoize or cache the results from the function call. Essentially you would need to make sure that if the input arguments are the same, the result will maintain reference equality with the previous result. This introduces the need to maintain some kind of cache state.
Level 3: createSelector
Fortunately, Reselect library, which is reexported with Redux Tookit, does this work for you. You can take a look at the redux toolkit for the syntax.
const selectFilteredItems = createSelector(
(state) => state.itemsArray, // the first argument accesses relevant data from global state
(itemsArray) => itemsArray.filter(checkCondition) // the second parameter conducts the transformation
)
//...
// somewhere inside a react component
const cart = useSelector(selectFilteredItems) // re-renders only when needed
Here the second function is called the transformation function, and is where we put the expensive computation, or the function that returns inconsistent references as a result (filter,map etc).
The createSelector caches
a) the arguments to the transformation function
b) the result of the transformation function
of the previous call to the selectFilteredItems function. If the arguments are the same, it skips executing the transformation function, and gives you the result you got the last time it was executed.
So when useSelector looks at the result, it gets reference equality. Hence the re-render is skipped!
One little caveat here is that createSelector only caches the very previous result. This makes sense if you think about a single component. In a single component you are only concerned about differences in values and results compared to the previous render. But in practice, you are likely to share selectors across multiple components. If this happens, you have a single cache location, and multiple components using this cache. i.e. Stuff breaks.
Level 4: createSelector factory function
Since the logic for your selector is the same, what you need to do is run createSelector for each component that uses it. This creates a cache for each component, giving us the desired behaviour. In order to do this, we use a factory function.
const makeSelectFilteredItems = () => createSelector(
(state) => state.itemsArray, // the first argument accesses relevant data from global state
(itemsArray) => itemsArray.filter(checkCondition) // the second parameter conducts the transformation
)
//...
// somewhere inside a react component
const selectFilteredItems = useMemo(makeSelectFilteredItems,[]); // make a new selector for each component, when it mounts
const cart = useSelector(selectFilteredItems) // re-renders only when needed
You intend to make a new selector (and by extension, cache) for each new component that mounts. So you put it inside the actual component function and not on the module scope. But this will re-run makeFilteredSelector for each render, and hence create a new selector for each render, and hence eliminate the cache. This is why you need to wrap the function in a useMemo with an empty dependency array. It runs on every mount.
And voila!
You now know where, why and how to use selectors in Redux. I personally feel that the createSelector syntaxes are slightly contrived. There is some discussion on changing cache sizes going on. But for now I feel that sticking to the docs should get you through most situations.
But I'm seeing all this information saying you should define your selectors beside your slice, use createSelector and all.
That's the way to go if you're deriving something from the state, which ends up being an expensive computation or something that's reused often throughout your app. Imagine, for example, your state.cart can contain 50.000 items and you need to sort them from most expensive item to least expensive. You don't want to re-calculate this all the time because it slows your app down. So you cache/memoize the result.
What is the right way, and why is it better?
The right way is to use memoization helpers like createSelector when/if you want to avoid expensive computation. Most people optimize prematurely, so I'd just stick to useSelector and keep it simple if in doubt.
I am new to Redux and Redux toolkit. I learned that createSelector can accept multiple input selectors, which can be provided as separate arguments or as an array. The results from all the input selectors are provided as separate arguments to the output selector.
const selectA = state => state.a;
const selectB = state => state.b;
const selectC = state => state.c;
const selectABC = createSelector(
[selectA, selectB, selectC],
(a, b, c) => {
// do something with a, b, and c, and return a result
return a + b + c;
}
);
my question is, if I only care about one simple state, can I just use useSelector like this
const selectA = state => state.a;
const a = useSelector(selectA)
what's the difference between the two usages?
A "selector" is any function that accepts the Redux state tree as an argument, and returns some extracted or derived data. That includes plain functions like you showed.
In many cases, you want to memoize the calculation of the results, such as mapping over an array of items, so that it's not re-calculated unless the inputs have changed. Reselect's createSelector creates memoized selector functions that only recalculate the output if the inputs change.
For more details, see my post Using Reselect Selectors for Encapsulation and Performance , as well as the "Performance and Normalizing Data" page in the new "Redux Essentials" core docs tutorial.
Use createSelector only if we want to memoise the function which retrieves store value.
Otherwise it's not required.
Lately I've been trying to write my React components as "Pure Functions" and I've noticed that sometimes I want to have something which feels a lot like state. I was thinking about passing my state as a second parameter to my component. I can achieve this by calling my component as a normal function with two parameters, props and state.
For example:
// abstracted to it's own module
const useState = (Component, state = {}) => {
return class extends React.Component {
state = createState(this, state); // will traverse and update the state
render() {
const { props, state } = this;
return Component(props, state); // <-- call the Component directly
}
};
};
const Component = (props, { index, increase }) => (
<div onClick={increase} {...props}>
Click me to increase: {index}
</div>
);
const componentState = {
index: 0,
increase: (event, state) => ({ ...state, index: state.index + 1 })
};
const StatefullComponent = useState(Component, componentState);
<StatefullComponent style={{ color: "purple" }} />;
I have a CodeSandbox example:
My questions are:
Will this pattern harm performance?
I'm no longer extending the props with state values, this might be a good thing
I am messing with the way components are rendered by default, this might be a bad thing
Will this Pattern break things like shouldComponentUpdate? (I have a sinking feeling this is modelling the old context api)
How worried should I be that future react updates will break this code?
Is there a more "Reacty" way of using State in a Pure function without resorting to libraries like Redux?
Am I trying to solve something which should not be solved?
Note: I'm using state in this example, but it could also be a theme, authorisation rules or other things you might want passed into your component.
EDIT 19-03-2018: I have noticed that people seem to be confused about what I'm asking. I'm not looking for a new framework or a conversation about "why do you want to separate your concerns?". I am quite sure this pattern will clean up my code and make it more testable and "cleaner" in general. I really want to know if the React framework will in any way hinder this pattern.
At first glanced when I checked your code I had a question:
"Why do you make it so complicated? When you can simply make it with a class declaration".
But later when I have splitted your code I found it really worth to do that.
Question 1: Doesn't really make a difference, it is the way how HOC does the composition.
I'm no longer extending the props with state values, this might be a good thing
Why/When might it be a good thing?
I am messing with the way components are rendered by default, this might be a bad thing
I don't see that you break or mess the rendering by default, I think the HOC pattern promotes the same philosophy, the difference you separate state from props.
Question 2: If a developer decide to use a stateless component then he/she should realize all “lifecycle methods” or references ref will be not available.
Your pattern make stateless component as “statefull” but in stateless declaration - amazing 😋.
Like in JSX you write in JS an "HTML" and inside it JS code with another "HTML":
<ul>
{list.map(text => <li>text</li>)} // I know there should be used key
</ul>
Mr. Baudin pattern (state-full like stateless):
import React from 'react'
import {useState} from './lib'
const state = {
index: 0,
increase: (event, state) => ({index: state.index + 1})
}
const Component = (props, state) => (
<div onClick={state.increase} {...props}>
Click me to increase: {state.index}
</div>
)
export default useState(Component, state)
Question 3: It depends what break changes will be in coming versions.
Question 4: Well... I don't think the offered pattern (implemented library) can be considered as application state management but it can be used within any state management like Redux or Mobx because it deals with internal component state.
Question 5: No, I don't think. Your solution makes code less and clean. Functional components are good for very simple or representational components and now it can be extended with state.
While this question has been open I've done some painful research on the subject and I'd like to share this research with you.
Question 1: Performance; Calling your components as functions or even as constructor functions doesn't really make a difference. You simply get your component instead of a type.
// the component
const MyComponent = () => (<div>This is my page</div>);
console.log(MyComponent());
console.log(new MyComponent());
console.log(<MyComponent />);
console.log(React.createElement(MyComponent));
Pen (Don't forget to inspect the developer tools!)
What I've noticed is that when you call a component directly you lose a little information, for example, when I use JSX the type information is preserved:
React.createElement(MyComponent).type === MyComponent // <- true
MyComponent() // <- Now way to find out what constructed this...
This doesn't seem like a big deal because the MyComponent() is seen as a normal div so it should render correctly; but I can imagine that React might do some lookup on the type of the component and calling your function like this that might interfere with the performance.
Haven't found anything in the documentation nor in the source code to suggest that this is the case, so I see no reason to worry about performance at this point.
Question 2: Does this break shouldComponentUpdate; the answer is "maybe not", but not because I need to write a class as was suggested. The problem is that React does a shallow compare on the props when you use a PureComponent and with pure functions just expects that with the same props you get the same result. In my case, because of the second parameter it might think the component doesn't need to update but actually it should. Because of some magic in my implementation this seems to work for child components of a root component wrapped with the useState function.
This is as I expected the same problem as with the original implementation of the context api. And as such I should be able to solve it using some reactive techniques.
Question 3: Seeing how "just calling a component as a function" seems to be the entire idea behind react and seeing how it results in almost exactly the same component without the original type information I see no reason why this should break in the future.
Question 4/5: No, there is no more "Reacty" way of really solving this problem. There is how ever a more functional way. I could use a state monad and lift the entire thing up; but that would envolve a lot of work and I really can't see the benefit of doing that. Passing state as a second parameter seems, at least for now, as something which might be strange but viable and actually feasable.
Question 5: When I started looking around I didn't find a lot os answers to these questions, but now that I've really dug myself in I can see a few other libraries doing the same thing. For example: recompose which calls itself "lodash for react". They seem to use this pattern of wrapping your component in a function and returning a class a lot. (Their withState implementation).
Extra information: My conclusion is that this pattern (because it's nothing more than a pattern) is valid and does not break any fundamental rules of React. Just to give a little bit of extra information Bernardo Ferreira Bastos Braga wrote that I needed to use a class to do it "the React way". I fail to see how wrapping your function and returning a class with state is anything other than "using a class".
I do however realise that wrapping a function increases complexity, but not by much; function calls are really optimised and because you write for maintainability and optimise later.
One of my biggest fears is that when the software gets more and more complocated and we get more cross-cutting concerns to deal with, it will get harder and harder to handle every concern as a parameter. In this case it might be good to use a destructuring pattern to get the concerns which you need from a "concerns" obejct passed as the second parameter.
One last thing about this pattern. I've done a small test (Just selenium rendering a page a 100 times) and this pattern, on a small scale, is faster than using Redux. The bigger your redux state gets and the more components you connect the faster this pattern becomes. The down side is that you are now doing a bit of manual state management, this comes with a real cost in complexity. Just remember to weigh all options.
A few examples of why this state component
Applications which interact with users, require that you try to keep track of the interactions they have. These interactions can be modeled in different ways but I really like a stateful approach. This means that you 'thread' state through your application. Now in react you have a few ways of creating components. The three I want o mention are:
create a class and extend from Component
create a class and extend from PureComponent
create a stateless function
I really like the last option but, to be honest, it's a pain keeping your code performant. There are a lot of articles our there explaining how lambda expression will create a new function every time your component is called, breaking the shallow compare of the props done by PureComponent.
To counteract this I use a pattern where I wrap my stateless component in a HoC where I pass my component and my state object. This HoC does some magic and passes the state as a second parameter to the stateless function, ensuring that when the props are tested by the compare of the PureComponent it should work.
Now to make the wrapper even better I memoize the lambdas so that only a single reference to that function exists so that even if you were to test the function by reference it should still be OK.
The code I use for this is:
return Object.entries(_state).reduce(
(acc, entry) => {
const [key, value] = entry;
if (value instanceof Function) {
acc[key] = _.memoize(item => (...args) => {
const newState = value.apply(null, [...args, root.state, root.props]);
root.setState(newState);
});
} else {
acc[key] = value;
}
return acc;
},
{}
);
};
As you can see I memoize the function and call it proxying the arguments and passing in the state and the props. This works as long as you can call these functions with a unique object like so:
const MyComponent = useState((props, { items, setTitle }) => {
return (
<div>
{items.map(item => (
<Component key={item.id} item={item} changeItem={setTitle(item)} />
))}
</div>
);
}, state);
1- Will this pattern harm performance?
Performance is usually not a black/white, rather it is better / worse in different scenarios. Since React already has a standard way of doing this, it it plausible that you'll be missing out on internal optimizations.
2-Will this Pattern break things like shouldComponentUpdate? (I have a sinking feeling this is modelling the old context api)
Yes, you should be using the class declaration if you need to write shouldComponentUpdate functions
3- How worried should I be that future react updates will break this code?
I think is fair to say that you should, since there are obvious and documented ways of doing the same using classes.
4 - Is there a more "Reacty" way of using State in a Pure function without resorting to libraries like Redux?
you could have a container component that has state and pass down callback functions to update the state
5- Am I trying to solve something which should not be solved?
yes, since there is already a mainstream and documented way of archieving what you need using the Component class. You should probably resort to functional components only for very simple or presentational components
Following the example on the reselect docs:
import { createSelector } from 'reselect'
const shopItemsSelector = state => state.shop.items
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((acc, item) => acc + item.value, 0)
)
In a typical redux app, subtotalSelector will recompute if a user updates item.name, even though this has no impact on the result. Is there a way to avoid this?
Two solutions:
Let it be. Unless you have a large number of items, the browser's computing capacity is well enough to handle the recompute.
Seperate prices from the item object. That is, you have state.shop.items.itemNames(containing id-name pairs) and state.shop.items.itemValues(containing id-value pairs). Then only the itemValues is passed to the selector.
I have a similar problem and I have found a sort of hack to get arround it.
I have a complex set of filters, and a huge number of items to filter through. Part of the filter state includes display state. I want to ignore changes in the display state so I don't filter a huge list all the time. This is an easy-ish solution:
const getFilters = createSelector(
state => state.filters,
filters => {
const filtersWithoutDisplay = {};
const ignoreObj = { collapsed: null };
for (let filterGroup in filters) {
filtersWithoutDisplay[filterGroup] = Object.assign({}, filters[filterGroup], ignoreObj);
}
// We create a new object every time, so this cannot be memoized properly unless we stringify.
return JSON.stringify(filtersWithoutDisplay);
}
);
It returns a JSON string that has to be parsed, but it's a primitive so as an input to another selector it doesn't trigger a recomputation if the actual contents don't change. That's kind of a hack.
You could also define an object outside of the selector function and always keep the same reference, change the insides according to this same patter, and then use a custom deep equality check by pulling in createSelectorCreator, as explained here https://github.com/reactjs/reselect#q-why-is-my-selector-recomputing-when-the-input-state-stays-the-same . This is probably a better way to go, but as it says:
Always check that the cost of an alternative equalityCheck function or deep equality check in the state update function is not greater than the cost of recomputing every time.
That goes for the JSON.stringify hack as well. I wouldn't do it for the huge list, but for the filters, sure.
In my situation, it's probably better to refactor my state because the filter values may be a separate concern from the filter display settings, and this may not be the only time I want them separate.
This question has been going round and round in my head since I read the release notes (and other related hype) around React 0.14 - I'm a big fan of React and I think that stateless components (https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components) are an excellent idea, both for the ease of writing such components and for expressing in code the intention that these components should be "pure" in terms of rendering consistently for the same props data.
The question is: how will it be possible for React to optimise these stateless component functions without going whole-hog and assuming that props references are not only immutable in that they shouldn't be manipulated within the component, but also that they can never change outside of the component lifecycle? The reason that "regular" components (aka stateful components - in other words, the components that go through the whole lifecycle; componentWillMount, getInitialState, etc..) have an optional "shouldComponentUpdate" function is that React does not assume that all props and state references are completely immutable. After components have been rendered, certain properties of the props references may change and so the same "props" instance may have different contents later on. This is partially why there was a lot of excitement over the use of fully-immutable structures and why it was said that using Om with React could offer great performance gains; because the immutable structures used there guaranteed that any given instance of any object could never be mutated, so shouldComponentUpdate could perform really cheap reference equality checks on props and state (http://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs/).
I've been trying to find out more information about this but haven't got anywhere. I can't envisage what performance improvements could be made around stateless components without presuming that props data will consist of immutable types.. maybe some preliminary analysis of non-immutable props types to try to guess whether "props" and "nextProps" represent the same data?
I just wondered if anyone had any inside information or some otherwise enlightening insights onto this. If React did start demanding that props types be "fully immutable" (allow reference equality comparisons to confirm that data has not changed) then I think that would be a great step forward, but it could also be a big change.
Since your component is just a pure function of its parameters, it would be straightforward to cache it. This is because of the well known property of pure functions, for same input they will always return same output. Since they only depend on their parameters alone, not some internal or external state. Unless you explicitly referred some external variables within that function that might be interpreted as a state change.
However, caching would not be possible if your function component reads some external variables to compose the return value, so that, those external variables might change over time, making cached value obsolete. This would be a violation of being a pure function anyways and they wont be pure anymore.
On React v0.14 Release Candidate page, Ben Alpert states:
This pattern is designed to encourage the creation of these simple components that should comprise large portions of your apps. In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.
I am pretty sure that he meant cacheability of pure functional components.
Here is a very straight forward cache implementation for demonstration purposes:
let componentA = (props) => {
return <p>{ props.text }</p>;
}
let cache = {};
let cachedA = (props) => {
let key = JSON.stringify(props); // a fast hash function can be used as well
if( key in cache ) {
return cache[key];
}else {
cache[key] = componentA(props);
return cache[key];
}
}
And there are other good properties of pure functional components that I can think of at the moment:
unit test friendly
more lightweight than class based components
highly reusable since they are just functions
Avoiding Unnecessary Allocations
If I understand correctly, stateless functional components are converted into regular components. From the source:
function StatelessComponent(Component) {
}
StatelessComponent.prototype.render = function() {
var Component = ReactInstanceMap.get(this)._currentElement.type;
return Component(this.props, this.context, this.updater);
};
When an instance of a stateless component is created, a new object is allocated. This new object has lifecycle methods such as componentWillMount and componentWillReceiveProps. I'm guessing that the plan is to not create these objects at all. Not creating the objects will avoid unnecessary allocations.
Avoiding Unnecessary Checks
Implementing the lifecycle methods requires a number of checks like this:
if (inst.componentWillUpdate) {
inst.componentWillUpdate(nextProps, nextState, nextContext);
}
Stateless functional components can be assumed to not have these lifecycle methods. That could be what the docs are referring to, but I'm not sure.
EDIT
Removed stuff on memoization, which didn't answer the question or explain stuff well.
You can use a decorator to compose your stateless function components to perform high order optimisation to determine if React should renders this component or not. I'm using immutable to perform strict equality checks between props.
Let's say we have this kind of component :
ClickableGreeter.js
const ClickableGreeter = (props) => (
<div onClick={(e) => props.onClick(e)}>
{"Hello " + props.name}
</div>
)
ClickableGreeter.propTypes = {
onClick: React.PropTypes.func.isRequired,
name: React.PropTypes.text.isRequired
}
export default ClickableGreeter;
We want React to not rendering it if the name does not change. I'm using a simple decorator that use immutable library to create immutable representation of props and nextProps and perform a simple equality check :
pureImmutableRenderDecorator.js:
import React from 'react'
import Immutable from 'immutable';
const pureComponent = (Component, propsToRemove = []) => {
class PureComponent extends React.Component {
constructor(props) {
super(props);
this.displayName = 'PureComponent';
}
comparator(props, nextProps, state, nextState) {
return (
!Immutable.is(Immutable.fromJS(props), Immutable.fromJS(nextProps)) ||
!Immutable.is(Immutable.fromJS(state), Immutable.fromJS(nextState))
)
}
removeKeysFromObject(obj, keys) {
var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target;
}
shouldComponentUpdate(nextProps, nextState) {
let propsToCompare = this.removeKeysFromObject(this.props, propsToRemove),
nextPropsToCompare = this.removeKeysFromObject(nextProps, propsToRemove);
return this.comparator(propsToCompare, nextPropsToCompare, this.state, nextState)
}
render() {
return <Component {...this.props} {...this.state} />
}
}
return PureComponent;
}
export default pureComponent;
Then, you can create a PureClickableGreeter component by doing :
const PureClickableGreeter = pureComponent(ClickableGreeter, ['onClick'])
//we do not want the 'onClick' props to be compared since it's a callback
Of course, using immutable here is overkill because it's a simple string comparison but as soon as you need some nested props, immutable is the way to go. You should also keep in mind that Immutable.fromJS() is a heavy operation, but it's fine if you do not have to many props (and that's normally the whole point of stateless functional components : to keep as many props as possible for better code splitting and reusability).
There's finally an answer! I'm not sure what version of React that this arrived in (I suspect that 0.14 did not include this but merely laid the groundwork) but now a PureComponent does not implement "shouldComponentUpdate" because it is handled automatically by React, which:
only shallowly compares the objects
This does mean that you have to be careful about using this type of component if you can't reliably detect changes with a shallow comparison but, if you can, it makes them very performant and can potentially avoid a lot of changes to the virtual DOM!
See here for more info ReactJs.org's 'React.PureComponent' section.