bitovi / bitovi/ylem

Updating props in ylem components

Open
#188 0 comments 0 reactions 0 assignees View on GitHub
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 `

{ foo }
`. If the parent is then updated to `{ foo: 'bar', bam: { foo: 'foo2', bar: 'bar' } }`, only the child component should re-render, as neither `foo` nor `bam` changed (though `bam`’s child info did). If we just merged keys over, it would cause extra re-renders

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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.