Problem
*That is not exactly my case but here is what matters.
Imagine an app that renders a list of elements:
const ElementsList = ({elements, <onElementTextChange>}) => (
<div className='ElementsList'>
{elements.map(el => (
<Element
key={el.id}
element={el}
/>
))}
</div>
)
Elements are kept in state of the parent component that renders ElementsList
. Each element is a simple object:
this.state = {elements: [
{id: 1, text: 'apple'},
{id: 23, text: 'banana'},
]}
...
render() { ... <ElementsList elements={this.state.elements}/> ... }
Each element renders with the input field nearby to change its text:
export const Element = ({element, <onTextChange>}) => (
<div className='Element'>
<div className='element-text'>{element.text}</div>
<input type='text' value={element.text} onChange={???}></input>
</div>
)
Consider that there can be a lot of elements (the more the app can handle the merrier).
Now the question is how should we go about updating texts through those inputs.
Solutions
I see 2 options (some combinations of them are possible):
1. Proper react way (is it?) - notify parent component about the change
- at Element:
onChange={evt => onTextChange(evt.target.value)}
- at ElementsList:
onTextChange={newText => onElementTextChange(el, newText)}
- at parent component:
onElementTextChange={ (element, newText) => {make new elements array with the updated element somehow; setState(elements)} }
That solution seems too awkward to implement. Also it is slow compared to the alternatives below. Events happening on input change (correct me if I'm wrong):
- notify parent component about the change through the chain of components
- parent component constructs new elements array and calls
setState
- the whole virtual DOM gets updated (including each element)
- updated virtual DOM is compared to the previous virtual DOM version
- one Element is updated in the real DOM
2. Update props of the Element directly and forceUpdate()
- rewrite Element to be a class instead of functional component
- at Element: onChange={evt => {element.text = evt.target.value; this.forceUpdate()}}
Events happening on input change:
- props.element.text is changed and
forceUpdate
is called - only one Element in the virtual DOM is updated
- updated subtree of the virtual DOM (one Element) is compared to the old virtual DOM
- one Element is updated in the real DOM
Questions
Currently I'm using an intermediate approach but more on the "proper react way" side. And it isn't fast enough.
I like the second option more, it requires less code and seems like it should work much faster (in theory).
- Will I get significantly better perfomance by using the second approach? (Remember that there are thouthands of elements)
- What specific problems this approach can lead to? Yeah, yeah, I know the code is less transparent and this not how it is meant to be.
- Do you see any other way to get the perfomance up? I'd be happy to try even less react-like suggestions, might consider giving up on react partially or entirely.