Skip to content

Commit

Permalink
fix use-memo
Browse files Browse the repository at this point in the history
  • Loading branch information
atellmer committed Jul 21, 2022
1 parent edaf181 commit 4430e0d
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 87 deletions.
49 changes: 27 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,12 @@ const App = createComponent(() => {
}, 1000);
}, []);

return [<div>app</div>, <MemoHardComponent />];
return (
<>
<div>app</div>
<MemoHardComponent />
</>
);
});

render(<App />, document.getElementById('root'));
Expand Down Expand Up @@ -518,6 +523,27 @@ Suitable for memoizing handler functions descending down the component tree:
);
```

#### useDeferredValue

Dark under the hood performs all recalculations in asynchronous mode so as not to block the main thread. Due to the task scheduler, you can achieve prioritization of interface updates. For example, make some updates more important than others, and vice versa - less important.

```tsx
import { useDeferredValue } from '@dark-engine/core';
```
This fixes an issue with an unresponsive interface when user input occurs, based on which heavy calculations or heavy rendering is recalculated.
Returns a delayed value that may lag behind the main value. It can be combined with each other and with useMemo and memo for amazing responsiveness results...

```tsx
const Items = createComponent(({ items }) => {
const deferredItems = useDeferredValue(items);
const items = useMemo(() => {
return deferredItems.map(item => <li key={item.id}>{item.name}</li>);
}, [deferredItems]);

return <ul>{items}</ul>;
});
```

### Refs

Refs are needed to be able to get a reference to a DOM element or a reference to a component in order to interact with them more subtly.
Expand Down Expand Up @@ -709,27 +735,6 @@ const App = createComponent(() => {
});
```

### Concurrent

Dark under the hood performs all recalculations in asynchronous mode so as not to block the main thread. Due to the task scheduler, you can achieve prioritization of interface updates. For example, make some updates more important than others, and vice versa - less important.

```tsx
import { useDeferredValue } from '@dark-engine/core';
```
This fixes an issue with an unresponsive interface when user input occurs, based on which heavy calculations or heavy rendering is recalculated.
Returns a delayed value that may lag behind the main value. It can be combined with each other and with useMemo and memo for amazing responsiveness results...

```tsx
const Items = createComponent(({ items }) => {
const deferredItems = useDeferredValue(items);
const items = useMemo(() => {
return deferredItems.map(item => <li key={item.id}>{item.name}</li>);
}, [deferredItems.length]);

return <ul>{items}</ul>;
});
```

### Portals

This is a browser environment-specific ability to redirect the rendering flow to another element in the DOM tree. The main purpose is modal windows, dropdown menus and everything where it is necessary to avoid the possibility of being overlapped by the parent container due to configured css overflow.
Expand Down
51 changes: 51 additions & 0 deletions examples/deferred.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { h, createComponent, useState, useMemo, useDeferredValue } from '@dark-engine/core';
import { render } from '@dark-engine/platform-browser';

function generateProducts() {
const products: Array<string> = [];

for (let i = 0; i < 10000; i++) {
products.push(`Product ${i + 1}`);
}
return products;
}

const dummyProducts = generateProducts();

function filterProducts(filterTerm) {
if (!filterTerm) {
return dummyProducts;
}

return dummyProducts.filter(product => product.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1);
}

type ProductListProps = {
products: Array<string>;
};

const ProductList = createComponent<ProductListProps>(({ products }) => {
const deferredProducts = useDeferredValue(products);
const items = useMemo(() => {
return deferredProducts.map(product => <li key={product}>{product}</li>);
}, [deferredProducts]);

return <ul>{items}</ul>;
});

const App = createComponent(() => {
const [name, setName] = useState('');
const deferredName = useDeferredValue(name);
const filteredProducts = useMemo(() => filterProducts(deferredName), [deferredName]);

const handleInput = e => setName(e.target.value);

return (
<div>
<input value={name} onInput={handleInput} />
<ProductList products={filteredProducts} />
</div>
);
});

render(<App />, document.getElementById('root'));
5 changes: 1 addition & 4 deletions packages/core/src/fiber/fiber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
getVirtualNodeKey,
detectIsVirtualNode,
detectIsVirtualNodeFactory,
detectIsEmptyVirtualNode,
} from '../view';
import { detectIsMemo } from '../memo';
import type { Context, ContextProviderValue } from '../context/model';
Expand Down Expand Up @@ -366,9 +365,7 @@ function mutateAlternate(options: PerformAlternateOptions) {
const hasAnyKeys = hasKeys || nextKeys.length > 0;

if (process.env.NODE_ENV === 'development') {
const isEmptyNode = alternate.child && detectIsEmptyVirtualNode(alternate.child.instance);

if (!hasAnyKeys && prevElementsCount !== 0 && !isEmptyNode) {
if (!hasAnyKeys && prevElementsCount !== 0 && nextElementsCount !== 0) {
error(`
[Dark]: Operation of inserting, adding, replacing elements into list requires to have a unique key for every node (string or number, but not array index)!
`);
Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/use-memo/use-memo.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { detectIsUndefined, detectIsDepsDifferent } from '../helpers';
import { type DarkElement } from '../shared';
import { detectIsUndefined, detectIsArray, detectIsDepsDifferent } from '../helpers';
import { detectIsComponentFactory, createComponent } from '../component';
import { detectIsTagVirtualNode } from '../view';
import { detectIsVirtualNodeFactory } from '../view';
import { componentFiberHelper } from '../scope';
import { Fragment } from '../fragment';
import { $$memo } from '../memo';

const Memo = createComponent(({ slot }) => slot, { token: $$memo });

function wrap<T>(value: T, isDepsDifferent: boolean) {
if (detectIsTagVirtualNode(value) || detectIsComponentFactory(value)) {
const factory = Memo({ slot: value });
const check = (value: T) => detectIsVirtualNodeFactory(value) || detectIsComponentFactory(value);

if (detectIsArray(value) ? check(value[0]) : check(value)) {
const slot = value as unknown as DarkElement;
const factory = Memo({
slot: Fragment({ slot }),
});

factory.shouldUpdate = () => isDepsDifferent;

Expand Down
Loading

0 comments on commit 4430e0d

Please sign in to comment.