How to manage complex list of items in react? - javascript

The bounty expires in 6 days. Answers to this question are eligible for a +200 reputation bounty.
Jeff is looking for a canonical answer:
the bounty will go to the answer that provides a detailed component design, describing the components and hooks needed to implement this. It would be nice to reference a sandbox or actual code, but not necessary.
I have a faceted search component that I am building. I am having trouble figuring out how and where to handle state.
Solution #1
A parent component manages state - uses a reducer to manage all components and pass state down
Cons: child components have a lot of complexity (autocomplete, focus state) and updating the state at the parent level causes child components to re-render. This means the child component's focus state and suggestions do not work well.
Pros: Somewhat cleaner design, state handled in one reducer
Solution #2
Child components manage their own state (like uncontrolled components). Parent component just manages creating, deleting new complex components. Children update a ref on the parent once they know they are completed (lose focus).
Cons: managing two pieces of state and duplicate key bugs
Pros: child components work as expected
Help and Suggestions
Both solutions are difficult to implement, but so far I am having better luck with solution #2.
Are there any good examples of this out there? It seems that editable todo lists would have a similar issues.
I like the idea of solution #1 but I think I would need a way to defer updating the state.

When your state is complex and used by multiple siblings/children, you definitely want that state to live on the common parent. Solution 2, where siblings each manage part of a complex state will become a nightmare to maintain - as you'd have to keep all those different states in mind while working one one.
Another thing to keep in mind is the shape of the state itself. For a state consisting of multiple types, useReducer() is going to be your better option.
For example; a complex state is :
... = usestate({
"user" : {
"name": "",
"age" : null},
skills: [],
rate: 50,
company: "default",
reviews: [],
});
To update this state, you'll have to write a lot of type checks and different types of spreads. Some are arrays, some int, some String, etc.
At that point, a reducer makes a lot more sense. A reducer handles the state by means of an action and a payload. The state logic then lives in a separate file for these components.
I think this is the way to go: https://www.aleksandrhovhannisyan.com/blog/managing-complex-state-react-usereducer/

Related

Will React functional component always re-renders as long as it has a child component?

I have been struggling with a performance problem in a React (React Native to be exact) app for days. It boils down to this problem:
When a Parent function component has another function component as the Child, the Parent will always re-renders whenever its own parent (GrandParent) re-renders. And this is true even if all components are memoized with React.memo.
It seems to be a fundamental problem for React, because in this common situation, HeavyParent will always re-renders whenever LightGrandParent re-renders, even if neither HeavyParent nor LightChild needs re-rendering. This is obviously causing a massive performance problem for our app.
<LightGrandParent>
<HeavyParent>
<LightChild />
</HeavyParent>
</LightGrandParent>
I can't believe this is how React works. Did I miss anything?
I created a sandbox to show what I mean.
A component re-renders if its state changes, or when its parent re-renders. Re-renders on state changes are normal. The other case sometimes is problematic. In this components tree you pasted
<LightGrandParent>
<HeavyParent>
<LightChild />
</HeavyParent>
</LightGrandParent>
LightGrandParent consumes HeavyParent with props.children, which gives you out of the box optimization, as React won't re-renders HeavyParent if LightGrandParent re-renders because of an internal state change.
Now, if you wanna also control re-renders because of LightGrandParent's parent re-rendering, you could use memo to memoize HeavyParent.
But this won't work because HeavyParent consumes LightChild as children, which is a new refrence on each render, and memo comparing new props with previous to do caching.
The answers #yousumar and a few other folks provided were correct. However, this also means a very surprising phenomenon demonstrated as follows:
I know many developers won't see this as a problem and will just say "hey this is how it works". But I personally think that this at least represents a shortcoming in React's fundamentals, as it violates the principle of least surprise. Also React's core doc should have pointed this out so that developers like me wouldn't get burned.

Prevent Vue.js re-rendering child components

I've got a complex component which does all its rendering in a render function. There are multiple parts to this, and different bits of the view get rendered - one of these things is a filter bar, showing the filters that have been applied.
What I'm noticing happening, is if I apply a filter which in turn presents this bar, it causes everything else to be fully re-rendered. This is causing a number of other issues and I need to try and stop it from happening.
I've never come across this issue when using normal templates as Vue seems to handle these very intelligently, but I have no idea how to tackle this. The only thing I can think of is setting a key on each thing I don't want re-rendered but not sure if this will a) solve the problem, and b) be possible for the content that is passed in through a slot
Has anyone else faced this issue, and if so how can it be solved?
I had a similar issue when using vuetify text inputs in a complex component which was causing the app to slow down drastically.
In my search I found this link which was specific to vuetify:
high performance impact when using a lot of v-text-field
then found out that this is actually a vue thing from this GitHub issue:
Component with slot re-renders even if the slot or component data has not changed
and there is plan to improve this in it is tracked here (vue 3 should resolve this issue):
Update slot content without re-rendering rest of component
so after reading through all these I found some workarounds that helped me a lot to boost the performance of my app, I hope these will help you as well:
divide that complex component into smaller ones specially when there is some bit of code that changes data that bounds to template causing re-rendering (put them in their own component)
I moved all data layer control to the vuex store, instead of using v-model every where and passing data as events and props, all the data is updating in the store through an action and read from the store through a getter. (from data I mean somethings that is being looped in the template in a v-for, API results, and so on... all of them is being set, updated and read through the store. my components still have the data object but only for the things related to the style and template control like a boolean to control a modal or an imported icon which is used in the template and alikes)
lastly I wrote a function called lazyCaller which its job is to update the values in the store with a delay (when immediate data update isn't necessary) to avoid rapid updates comping from something like a text input (with out this every key stroke trigger the value update action)

React Having Two classes with Two sets of state

I am new to React. I have a small app I'm designing with a schedule and with list of products. Please see my component diagram, below.
I propose that The Schedule Controller and Product Control each will be a class with state.
I brought all the Product modules (a listing component and its children, A new product form, a product detail module, and an edit product module) to be direct children of the Product Controller to prevent excess prop drilling, so they can all pull from the product control class state.
So, as I'm thinking about this, I'm mindful of a single source of truth and to concentrate a single state. However, I've always done that because the whole app has one class.
It seems to me that, to obey those laws (and without using a store as in Redux, which I'm not ready to work my way up to yet. This is a learning exercise and I want to get some proficiency with regular React before adding a whole new library to my learning) I should pick the highest parent component and make only one class with one state.
It seems to me that when I use setState(), its going to the use the state of the class it inherits from. Right? And that state isn't going to get confused about which state to use, because its going to the parent class (I'm totally out on a limb here). The product modules will be extended from the Product class and setState() will only go up the chain to the parent and the parent state.
Or is the correct approach to squash the schedule modules as children of the product controller?
I did a lot of looking around and the references I keep finding are much more complex than what I'm attempting here (involving two different classes with state that DO share information, that's not happening here). I may not be searching the correct terms. So I'm sorry if this is something well known.
So the question I have is, is this a bad idea or bad practice?
A setState call in a class Component only sets the state of itself.
There are two forms of class state
State-> This will be the local state of the class. It will be changed and maintained by the class which declares it.
Props-> These are states/properties which are passed from the parent to the child class. This value cannot the changed. Only the parent class which owns these props has the power to change it. The only way to change this would be through lifting the state. You are right here.
Refer https://reactjs.org/docs/lifting-state-up.html
marzelin had the answer in the comments above. Thanks!

What do the React docs mean by the following statement?

In our experience, thinking about how the UI should look at any given moment, rather than how to change it over time, eliminates a whole class of bugs.
From React Docs
From my understanding, this means that React only updates what's necessary, rather than destroying and re-constructing the entire DOM tree again. Am I wrong?
Can anyone please help me understand the quoted statement?
Thanks.
From my understanding, this means that React only updates what's necessary, rather than destroying and re-constructing the entire DOM tree again. Am I wrong?
If you want to know the short answer, I have to say it is true, React will update the necessary elements in DOM whenever it needed.
But if you want to know how it's done, and when React will update the DOM and its element I have to it is varying to different several things, like project architecture, using proper methods (proper hooks in functional component eg. useCallback, useMemo, and so on) and so on.
When it truly gets rerender then?
As far as I know, there are two ways React finds out when to rerender the DOM.
Passing elements to ReactDOM.render
Update a state
What is ReactDOM.render?
This will call the render() method from react-dom (Where we usually import it as ReactDOM) and it will render a React element into the DOM in the supplied container and return a reference to the component (or returns null for stateless components). Also if the React element was previously rendered into the container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React element.
What does state mean?
The state object is where you store property values that belong to the component. So when you got a component and that component has their own specific variables where changing them should affect the DOM you should use it, then whenever state gets changes the component will be updated.
So what I even talk about project architecture and this stuff, when I didn't mention anything about it above?
Let's say we got a project with one parent component and 3 child component just like this:
Component 1
- |- Component 2
- - |- Component 3
- - - |- Component 4
So whenever you a state in Component 4 all of the DOM elements will be get rerendered, why then? Because Component 4 is a child of Component 3 and so on, so when the child state gets change it will force the parent to rerender then the whole DOM will rerender once per state change.
Final Note
So, at last, we should be always considered a good architecture and hierarchy for our project or when it necessarily use built-in methods like useMemo to avoid such a thing.

How to disable data observation in vue.js component property

I want to create a Vue.js component that receives properties from its parent component, e.g.:
<table-cell :value="foo" format="date" />
Because value and format are defined as properties, Vue will automatically register observers to their values. That is fine in general, but for my use case I positively know those values are not going to change so they don't need to be observed.
Given that my table cell component can be in a table with, say, 1,000 rows and 10 columns, those 2 properties will create 20,000 observers, and I want to avoid all this overhead (and my real table cell component has more complex properties).
Is there any way to disable a component property from being observed in order to avoid wasting CPU & memory resources?
Update:
I have found a low-level solution with the functional component approach, explained here: https://v2.vuejs.org/v2/guide/render-function.html#Functional-Components
I have tested it with this JSFiddle: https://jsfiddle.net/50wL7mdz/12143/
I wonder if that is the correct approach...
Pass it with custom data I think like <your-component :data-value='foo' :data-format='date'>
It will do what you want.

Categories