You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This can be used instead of `useSelector` for some cases.
369
+
370
+
#### For experts
371
+
372
+
`useTrackedState` doesn't have [the technical issue](#stale-props-and-zombie-children) that `useSelector` has.
373
+
This is because `useTrackedState` doesn't run selectors in checkForUpdates.
374
+
375
+
### What are the differences in behavior compared to useSelector?
376
+
377
+
#### Capabilities
378
+
379
+
A selector can create a derived values. For example:
380
+
381
+
```js
382
+
constisYoung=state=>state.person.age<11;
383
+
```
384
+
385
+
This selector computes a boolean value.
386
+
387
+
```js
388
+
constyoung=useSelector(isYoung);
389
+
```
390
+
391
+
With useSelector, a component only re-renders when the result of `isYoung` is changed.
392
+
393
+
```js
394
+
constyoung=useTrackedState().person.age<11;
395
+
```
396
+
397
+
Whereas with useTrackedState, a component re-renders whenever the `age` value is changed.
398
+
399
+
#### Caveats
400
+
401
+
Proxy-based tracking has limitations.
402
+
403
+
- Proxied states are referentially equal only in per-hook basis
404
+
405
+
```js
406
+
conststate1=useTrackedState();
407
+
conststate2=useTrackedState();
408
+
// state1 and state2 is not referentially equal
409
+
// even if the underlying redux state is referentially equal.
410
+
```
411
+
412
+
You should use `useTrackedState` only once in a component.
413
+
414
+
- An object referential change doesn't trigger re-render if an property of the object is accessed in previous render
415
+
416
+
```js
417
+
conststate=useTrackedState();
418
+
const { foo } = state;
419
+
return<Child key={foo.id} foo={foo} />;
420
+
421
+
constChild=React.memo(({ foo }) => {
422
+
// ...
423
+
};
424
+
// if foo doesn't change, Child won't render, so foo.id is only marked as used.
425
+
// it won't trigger Child to re-render even if foo is changed.
426
+
```
427
+
428
+
It's recommended to use primitive values for props with memo'd components.
429
+
430
+
- Proxied state shouldn't be used outside of render
431
+
432
+
```js
433
+
conststate=useTrackedState();
434
+
constdispatch=useUpdate();
435
+
dispatch({ type:'FOO', value:state.foo }); // This may lead unexpected behavior if state.foo is an object
436
+
dispatch({ type:'FOO', value:state.fooStr }); // This is OK if state.fooStr is a string
437
+
```
438
+
439
+
It's recommended to use primitive values for `dispatch`, `setState` and others.
440
+
441
+
#### Performance
442
+
443
+
useSelector is sometimes more performant because Proxies are overhead.
444
+
445
+
useTrackedState is sometimes more performant because it doesn't need to invoke a selector when checking for updates.
446
+
447
+
### What are the limitations in browser support?
448
+
449
+
Proxies are not supported in old browsers like IE11, and React Native (JavaScript Core).
450
+
451
+
However, one could use [proxy-polyfill](https://github.com/GoogleChrome/proxy-polyfill) with care.
452
+
453
+
There are some limitations with the polyfill. Most notably, it will fail to track undefined properties.
454
+
455
+
```js
456
+
conststate= { count:0 }
457
+
458
+
// this works with polyfill.
459
+
state.count
460
+
461
+
// this won't work with polyfill.
462
+
state.foo
463
+
```
464
+
465
+
So, if the state shape is defined initially and never changed, it should be fine.
466
+
467
+
`Object.key()` and `in` operater is not supported. There might be other cases that polyfill doesn't support.
468
+
293
469
## Custom context
294
470
295
471
The `<Provider>` component allows you to specify an alternate context via the `context` prop. This is useful if you're building a complex reusable component, and you don't want your store to collide with any Redux store your consumers' applications might use.
0 commit comments