7 ms·
A pain point with React is large data structures. To re-render (assuming class components), you can setState with the changed property. For example if your stat
by petilon 4y ago
A pain point with React is large data structures. To re-render (assuming class components), you can setState with the changed property. For example if your state has two properties named foo and bar, and bar has changed then you call setState({ bar: newValue }). This works if you have simple properties. What if you have large complex data structures, and you need to modify a property deep down inside? Then you can make a copy of the data structure, then modify the field in the copy, then call setState({ bar: copyOfLargeObject }). But it is tremendously wasteful to make a complete copy of a large data structure!
A workaround is to not make copies of large data structures. Just modify the large object directly. Then just call setState({}); That's right... setState() with an empty object triggers a re-render. Now you don't even have to store the object being modified in state. You can hold the large object in a member field of the class. So even though your component is stateful, you are not telling React what your state fields are - you are managing it yourself. At this point, React's programming model has broken down.
- acemarke 4y agoFWIW, React has always been designed around some Functional Programming type principles, such as immutable updates. Immutable updates do in fact require that if you want to update `state.some.nested.field`, you have to make copies of _all_ objects in that path: `nested`, `some`, and `state`. This isn't unique to React. Yes, class component `this.setState()` lets you get away with mutations. That's not really a _good_ thing. If anything, it's a holdover from an earlier era of JS, where it was much more common to use React with data structures that might be mutable. With the `useReducer/useState` hooks, the React team explicitly designed them to require immutable updates and pass in new references, otherwise they'll bail out, assuming that since it's the same reference nothing was changed and no render is needed. Some more details: - https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#immutability-and-rerendering https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-... - https://beta.reactjs.org/learn/updating-objects-in-state https://beta.reactjs.org/learn/updating-objects-in-state
- AgentME 4y agoWhen updating deeply-nested immutable objects, the Immer library is great. You call the `produce(immutableValue, draft => { ... })` function with some immutable value and a callback function that manipulates a mutable proxy object mimicking the immutable value, and then the function returns a new immutable value with the same changes made by the callback function. The mutable proxy object never escapes the callback, so the use of Immer stays self-contained as an implementation detail of your code without infecting your component's public API or anything. Another alternative is the "immutability-helper" library, which was originally published by the React team as "react-addons-update". It's a lighter library with less proxy magic going on, but at the cost of being more explicit: instead you create an object describing how to update an immutable value to produce a new immutable value. I've used it in the past in a few places but mostly recommend Immer over it now.
- acemarke 4y agoYeah, we specifically built Redux Toolkit around Immer from the very first prototype that I wrote. I had catalogued _dozens_ of immutable update libs between 2015-2018, and Immer is simply superior to all of them. Which is why Immer is a non-negotiable part of RTK, and something we specifically tell everyone they should be using with Redux: - https://redux.js.org/style-guide/#use-immer-for-writing-immutable-updates https://redux.js.org/style-guide/#use-immer-for-writing-immu... - https://redux-toolkit.js.org/usage/immer-reducers https://redux-toolkit.js.org/usage/immer-reducers (in fact, I recently got quoted on the top of the Immer docs with a tweet I wrote saying how awesome Immer is :) https://immerjs.github.io/immer/ https://immerjs.github.io/immer/ )
- TurningCanadian 4y agoWhere will the Record & Tuple proposal (Stage: 2) fit in? https://tc39.es/proposal-record-tuple/tutorial/ https://tc39.es/proposal-record-tuple/tutorial/
- acemarke 4y ago
- hither_shores 4y ago> But it is tremendously wasteful to make a complete copy of a large data structure! You don't make a complete copy: you make a shallow copy of the spine, and then only descend along the fields you actually modify. The number of operations scales as O(branching factor * depth), not O(size).
- pseudoramble 4y agoI decided that this was what I would do on my most recent work project. My quest to reduce the number of libraries it depends on led me to deciding against Immer or something like it. It works out cause I only use state of this complexity in a single spot. The code to do the copying does not read well in my opinion, and it definitely could get nasty in some cases (slower systems with updates on each keystroke say). A library like Immer seems very reasonable if you are working on big nested structures often. Worth considering depending on the case.
- petilon 4y ago> You don't make a complete copy: you make a shallow copy of the spine, and then only descend along the fields you actually modify. Sounds painful.
- Jasp3r 4y agoUse MobX https://github.com/mobxjs/mobx https://github.com/mobxjs/mobx
- brundolf 4y agoMobX formalizes this way of working with state and makes it non-fragile (and more performant to boot) I don't think I could recommend doing it the way you describe because there are a huge number of ways it can go wrong