0

This question is about internals for partial re-renderings with React-Redux.

To explain what I mean, I will first introduce a very crude technique for managing state without any state management libary. The technique uses a a huge "AppState"-object that is owned by the top-level App-component. Suppose that this AppState holds not only state-properties, but also several callbacks that mutate those state-properties. Furthermore, suppose that we use props to pass down this AppState throughout the entire component hierarchy. Thanks to the ES6-spread syntax, passing a huge number of props can be done without a lot of boilerplate code. In the top-level App-component, it will look like this:

<ChildComponent {...this.state} />

In all other components, it will look like this:

<GrandChildComponent {...this.props} />

It is important to note that the ES6-spread syntax does not actually pass the AppState-object. Instead, it extracts all the AppState-properties and passes them as separate props.

Now we should distinguish between top-level properties and nested child-properties of the AppState:

  • If I mutate a top-level property of this AppState by calling setState, then the entire app will re-render (unless I use things like pure components).
  • However, if I change a nested child-property of this AppState, then nothing will happen because React does not notice the property change.

This leads to my final questions:

  • What is the render-performance of this crude approach in comparison to Redux?
  • How exactly does Redux handle "partial renderings", such that only some of the Components re-render after a state mutation?
Mike76
  • 899
  • 1
  • 9
  • 31
  • 1
    First of all, [tag:spread-syntax] is [not an operator](https://stackoverflow.com/a/44934830/1541563). Secondly a spread property in JSX does not copy the object passed down, it passes the same reference. See this [babel output](https://babeljs.io/repl#?code_lz=DwMQ9mAEDeB08CMCGAnAvpA9APiA). – Patrick Roberts Apr 10 '20 at 02:59
  • It does not copy any object, but it only passes the individual properties. In other words: The original AppState object gets lost, which can be relevant if there are prototype functions on the original AppState object. – Mike76 Apr 10 '20 at 03:03
  • "It does not copy any object, but it only passes the individual properties". Those are contradictory statements, only one of those can be true. I've quite conclusively demonstrated showing babel output that the object reference is preserved when passed down. – Patrick Roberts Apr 10 '20 at 03:05
  • It passes the individual top-level properties, but it does not clone the sub-properties of the individual properties. Therefore, the individual top-level properties are taken without any further deep-copy. – Mike76 Apr 10 '20 at 03:08
  • You're not making any sense whatsoever, there is no copy operation performed. A reference is passed down, and the entire prototype chain is preserved. – Patrick Roberts Apr 10 '20 at 03:10
  • If the prototype chain was preserved, then how would you explain this question?: https://stackoverflow.com/questions/60896134/passing-classes-as-react-props-which-of-the-two-implementations-is-correct – Mike76 Apr 10 '20 at 03:14
  • 2
    After running a debugger, it appears the internal implementation of `React.createElement()` performs a copy of the object's own properties to a separate plain object, but this is unrelated to the spread element in the JSX syntax. A subtle but important distinction. It appears you are correct about that at least. – Patrick Roberts Apr 10 '20 at 03:33
  • If this is true, then this would demand a completely new answer for this question. I would ask you to please provide a detailed analysis for https://stackoverflow.com/questions/60896134/passing-classes-as-react-props-which-of-the-two-implementations-is-correct Then I would have to uncheck and dismiss the currently accepted answer.. Which is pretty shocking for my JavaScript knowledge.. – Mike76 Apr 10 '20 at 03:37
  • 1
    I've left a comment on their answer. I don't think it's necessary to unaccept it, the rest of the explanation is correct and helpful. The claim that react is unrelated just needs to be removed. – Patrick Roberts Apr 10 '20 at 03:54
  • 1
    @Mike76 Asking for a pattern versus another is highly opinion based and will likely always get closed. Though, since you've changed the direction you'd like to go with your edit, I removed my downvote and voted to reopen, as an explanation of partial-rerendering with redux can be answered with facts! – Emile Bergeron Apr 10 '20 at 18:23
  • As for the performance impact, I always say to avoid wasting time with early optimizations and focus on the end result. If a performance bottleneck **can be measured**, then look into solutions. – Emile Bergeron Apr 10 '20 at 18:25
  • I like to study performance impact just for the sake of understanding a framework. Actually, my "proposed" pattern is not a serious pattern. It just demonstrates my thought process on how someone might build an app without any state management library. – Mike76 Apr 10 '20 at 18:35
  • For the sake of learning, it's really good to dive into a framework and test different solutions for a common situation. Though questions about this aren't really meant for Stack Overflow. Maybe [Software Engineering Stack Exchange](https://softwareengineering.stackexchange.com/) would be a better place to ask about preferred patterns and performance, but I'm not even sure! :P – Emile Bergeron Apr 10 '20 at 18:58
  • I would say that "Partial Re-renderings with Redux" is specific enough for StackOverflow. StackOverflow dislikes questions that are not about a specific framework or code, but this seems to be specific. – Mike76 Apr 10 '20 at 19:42

2 Answers2

2

If I mutate a top-level property of this AppState by calling setState, then the entire app will re-render (because everything depends on the AppState).

If you mutate and use pure components then nothing will render, you change state by creating a new state object.

However, if I mutate a nested child-property of this AppState, then nothing will happen because React does not notice the property change.

This is only true if you mutate and components are pure.

What is the render-performance of this crude approach in comparison to Redux?

Prop drilling will re render the entire tree but branches that use state that didn't change won't re render if they are pure. Prop drilling is bad for maintenance because if you need to refactor grand child state logic you may need to refactor the whole tree or branch. But from a performance point it would not take a big hit provided that you use pure components and are careful when passing callbacks and not re creating them on every render (see useCallback).

How exactly does Redux handle "partial renderings", such that only some of the Components re-render after a state mutation?

React-redux useSelector or connect mapStateToProps are always called every time dispatch changed state and before rendering.

If the result is different than last result then react-redux will trigger render of the component. If the component gets props then a render could also be triggered because props change and mapstate/selector will be executed.

A connected component will observe state and render when the result of mapState or selector has changed. An example app with logs showing what react-redux will execute can be found here

HMR
  • 37,593
  • 24
  • 91
  • 160
0

For state management, you don't necessarily have to use Redux, if your use cases are small, maybe React Hook would be perfect for you.

For React rerendering matter, what I know is there are several strategies (useMemo, PureComponents) provided by React for managing and improve the performance. It really depends on how you manage your components.

One example is using PureComponent, even if you have a large state in your top-level app.js, if you manage the child components properly, they will not re-render if their receiving props haven't changed.

Jee Mok
  • 6,157
  • 8
  • 47
  • 80
  • 1
    How should React Hooks help for state management? In my understanding, "useState" is just a technique to make function components stateful, but it is not useful for sharing state between multiple components. – Mike76 Apr 10 '20 at 03:30
  • 2
    @Mike76 you can create a custom hook for global usage, or using context to avoid props drilling, but that's all depends on what your application is needing. Your question here is about React's performance on rerendering child components right? that could be solved by using pure components. It'd be easy to discuss if you have a sample code, and we can provide a solution to that sample – Jee Mok Apr 10 '20 at 03:34
  • 1
    Well, if I have a custom hook for global usage, then this is great for code sharing, but not so good for state sharing (because the state of custom hooks would be duplicated, right?). My question is mainly about "partial renderings" with Redux. I have not yet thought a lot about pure components. – Mike76 Apr 10 '20 at 03:42
  • @Mike76 for me I never have a single large redux store even for big application, so I'm not sure what exactly you're facing right now... but yeah pure components is something that I'd always extend from (usually) if I'm using class component (instead of extending React.Component) for this purposes. – Jee Mok Apr 10 '20 at 04:37