Spike: smarter updating of listeners
davidgilbertson opened this issue · 0 comments
Part 1 - parent path listeners
Currently, if there are two listeners:
<ParentComponent>
listening tostore.tasks
<ChildComponent>
listening tostore.tasks.1.done
Then if the prop at path store.tasks.1.done
is updated, then both components are updated. I did this for a specific reason in the early days and really should have documented why.
Edit, this is because a child component will often not get its data from the store passed by collect()
- it will get the data passed from the parent (e.g. a <TaskList>
will pass each task object from an array to a child <Task>
component). So if I don't update the <TaskList>
component with the new store, it wouldn't know if a task was ticked.
At the very least I need to add a test to protect/document this.
Part 1 result: no change to code, but a change to guidance:
"You don't need to wrap a component in collect
unless you want access to the store in that component". Two reasons for this:
- If
<TaskList>
referencesstore.tasks
and renders a bunch of<Task>
components, and the<Task>
component is not wrapped incollect
, then all of the reads in the<Task>
components (store.tasks.0.name
, etc) are attributed to the<TaskList>
- so it's listening on everything its children rendered. - And a second reason I've just forgotten.
Also, if you aren't wrapping your components in collect()
make sure you're using PureComponent
or React.memo
. To not use these is to be slow for no reason.
Part 2 - child path listeners
I also update down the prop tree. E.g. if a prop store.tasks
changes (an entire array is overwritten with a new array), then I'll trigger updates on components listening to paths that start with that. So, store.tasks.0
, store.tasks.0.done
and so on.
In other words, if you're listening to any prop 'inside' a prop that changes, you need to know about that parent prop changing. But, if a listener is listening to a child prop and is a child component then it won't need to update, because the parent component will update. But I have no way of knowing the relationships between components.
I don't think there's much I can do here. And, I think this falls under the control of React, which will not be wasteful even though I've requested redundant updates.
Part 2 Result: no change
Part 3 - tracking previous value
What if, for each listener, I stored the last value at the prop path a component is listening to? Then, when updating, if the value hasn't changed, don't bother updating. Would this catch anything?
- For updating listeners with an exact match on the path, it makes no difference (the proxy handler won't trigger an update if the value didn't change).
- For listeners to a parent prop path, it won't matter, because if a value changed for any prop path, then all parent objects in the store will be different (because immutability).
- But for listeners to a child path (I overwrite
store.data
and some component is listening tostore.data.page.title
), then if the title didn't change, I don't need to update it. But a component listening tostore.data.page.title
must also be listening onstore.data.page
and that will be a new object.
Also, would this take a lot of memory?
Part 3 Result: no change
Part 4 - listening on objects and arrays
Here's a question: do I need to listen to props that are objects/arrays? (And I mean {}
objects, not null, etc). You can't actually use an array or an object without accessing one of its child properties.
I mean, you can't render a task
to the screen. You can only render task.done
and task.name
and task.id
and so on. Even if you called JSON.stringify(task)
that calls everything under the hood.
Hmmm, I think I do need to listen to objects/array, because of part 1 above. I might only read an object in a parent component (store.page
), then pass that object to a child component (regardless of whether it's wrapped in collect
or not) and it reads a property that's a string page.title
. So yeah, I need to be listening for changes to the object, because Recollect might not be aware of child components listening to child props.
Part 4 result: no change
Well this is good and bad. I think it's about it's efficient as it can be regarding updating, unless I can think of a way to not trigger an update on a child component if I'm updating a component further up the tree (and only bother if React is handling this).