Updating props in ylem components
- Dominant language
- JavaScript
- Stars
- 42
- Forks
- 4
- PR merge metrics
- No merged PRs in 30d
Description
Ylem is trying to make rendering react components more efficient and solve the following rendering problem:
When rendering an ylem component, you might have props of `{ foo: 'bar', bam: { foo: 'foo', bar: 'bar' } }`. Your main component might use foo, then pass all of bam down to the child (which is also an ylem component), ala `
Currently, in order to handle situations like this a complex deep diff is performed of the new and old props inside the `updateObservableWithProps` function.
The new strategy will involve moving all diffs into the `deriveUpate` functions inside `./derivers.js`, or inside a custom derive update function the user passes into `connect`. Those `deriveUpdate` functions will now return patches (resulting from the diff), not objects.
```js
// ./derivers.js
function controlled(nextProps) {
// always use the latest props, overwiting existing data
return [
...canDiff.map(lastProps || {}, nextProps).filter(({ type }) => type === 'delete'),
...canDiff.map({}, nextProps),
];
}
function uncontrolled(nextProps, lastProps) {
// use props on initial render (when lastProps is null)
if (lastProps === null) {
return canDiff.map({}, nextProps);;
}
// otherwise do nothing
return null;
}
function changes(nextProps, lastProps) {
// use props on initial render (when lastProps is null)
return canDiff.map(lastProps, nextProps);
}
```
Then, instead of doing a `canDiff.deep` diff inside the `updateObservableWithProps` function, that function will instead simply apply the patches that the `deriveUpdate` function produced.
```js
// ./lib/observable-component.js
function updateObservableWithProps(observable, patches, observer) { // pass in "patches" instead of newProps
...
// remove deep dif
for (const { key, type, ...values } of patches) {
if (type === 'delete') {
// do the delete
}
if (type === 'splice') {
canReflect.splice(prop, values.index, values.deleteCount, values.insert);
}
if (type === 'add' || type === 'set') {
if (typeof values.value === 'object' && !React.isValidElement(values.value) && !Object.isExtensible(values.value)) {
if (canReflect.isMoreListLikeThanMapLike(values.value)) {
values.value = [ ...values.value ];
}
else {
values.value = { ...values.value };
}
}
canKey.set(observable, key, values.value);
}
}
...
}
```
Now all the responsibility for deciding what needs to be updated is left to the `deriveUpdate` functions.
Contributor guide
Research direction
Start in ./derivers.js and inspect the controlled, uncontrolled, and changes deriveUpdate functions, then trace how connect supplies custom deriveUpdate functions. Read updateObservableWithProps in ./lib/observable-component.js and verify that it receives and applies patches, with the deep diff removed; completion means deriveUpdate functions produce patches and nested prop changes update only the affected component.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, react
- Domain
- frontend
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100