Skip to content

Commit a95d578

Browse files
rolandszokesolkimicreb
authored andcommitted
feat(stores): allow function store param, use returned value
1 parent 4925f8a commit a95d578

File tree

4 files changed

+59
-8
lines changed

4 files changed

+59
-8
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,26 @@ export default view(() => {
503503

504504
</details>
505505

506+
<p></p>
507+
<details>
508+
<summary>You can also pass a <code>function</code> to the store. In this case easy-state will create a state store from the function result.</summary>
509+
<p></p>
510+
511+
```jsx
512+
import { store, view } from 'react-easy-state';
513+
514+
const localStore = () => ({ name: 'Bob' });
515+
516+
export default view(() => {
517+
const person = store(localStore);
518+
return <div>{person.name}</div>;
519+
});
520+
```
521+
522+
This is useful for large local stores to avoid large object creation on every render.
523+
524+
</details>
525+
506526
#### Local stores in class components
507527

508528
```jsx

__tests__/store.no-hook.test.jsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,29 @@ describe('Using an old react version', () => {
7373
'You cannot use state inside a render of a class component. Please create your store outside of the render function.',
7474
);
7575
});
76+
77+
test('Using function parameter for store should use the returned object', () => {
78+
const person = store(() => ({ name: 'Bob' }));
79+
const MyComp = view(
80+
class extends Component {
81+
render() {
82+
return <div>{person.name}</div>;
83+
}
84+
},
85+
);
86+
const { container } = render(<MyComp />);
87+
expect(container).toHaveTextContent('Bob');
88+
});
89+
90+
if (!process.env.NOHOOK) {
91+
test('Using function parameter for store inside function component should use the returned object', () => {
92+
const localStore = () => ({ name: 'Bob' });
93+
const MyComp = view(() => {
94+
const person = store(localStore);
95+
return <div>{person.name}</div>;
96+
});
97+
const { container } = render(<MyComp />);
98+
expect(container).toHaveTextContent('Bob');
99+
});
100+
}
76101
});

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/store.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useRef } from 'react';
1+
import { useMemo } from 'react';
22
import { observable } from '@nx-js/observer-util';
33

44
import {
@@ -12,13 +12,13 @@ export function store(obj) {
1212
// if it is a local store in a function component
1313
// create a memoized store at the first call instead
1414
if (isInsideFunctionComponent) {
15-
// we have to use useRef instead of useMemo for local store persistence
16-
// useMemo does not have the same guarantees about persistence as refs
15+
// useMemo is not a semantic guarantee
16+
// In the future, React may choose to “forget” some previously memoized values and recalculate them on next render
1717
// see this docs for more explanation: https://reactjs.org/docs/hooks-reference.html#usememo
18-
const ref = useRef(obj);
19-
// observable wrapping is idempotent
20-
// wrapping the same object multiple times is the same as wrapping it once
21-
return observable(ref.current);
18+
return useMemo(
19+
() => observable(typeof obj === 'function' ? obj() : obj),
20+
[],
21+
);
2222
}
2323
if (isInsideFunctionComponentWithoutHooks) {
2424
throw new Error(
@@ -30,5 +30,5 @@ export function store(obj) {
3030
'You cannot use state inside a render of a class component. Please create your store outside of the render function.',
3131
);
3232
}
33-
return observable(obj);
33+
return observable(typeof obj === 'function' ? obj() : obj);
3434
}

0 commit comments

Comments
 (0)