Optimization in use_reducer
Description
Yew currently provides two versions of the reducer hook:
use_reducer: This hook always causes a re-render when an action is dispatched.use_reducer_eq: This hook avoids the re-render if the new state is the same as the old state. However, the state now has to implementPartialEq.
I feel like there is room for a middle ground: The reducer takes an Rc and returns an Rc. Even if we don't have a PartialEq implementation on the state, we can still know that the state must be unchanged if the reducer returns the exact same Rc back to us. This check is almost free (just a pointer comparison).
This PR adds a simple check if the two Rcs are the same, and in that case skip the re-render.
Note that even for use_reducer_eq this check is beneficial, since the PartialEq implementation might be more expensive than just a pointer comparison.
This change might be a breaking change if someone was relying on the exact number of re-renders, but I'd assume that this is not covered in Yew's semver guarantees, right?
Checklist
- [x] I have reviewed my own code
- [x] I have added tests
Visit the preview URL for this PR (updated for commit 5e3dadf):
https://yew-rs-api--pr3945-use-reducer-optimiza-u4xs77n3.web.app
(expires Tue, 25 Nov 2025 22:37:31 GMT)
🔥 via Firebase Hosting GitHub Action 🌎
Benchmark - core
Yew Master
vnode fastest │ slowest │ median │ mean │ samples │ iters
╰─ vnode_clone 2.67 ns │ 3.139 ns │ 2.675 ns │ 2.684 ns │ 100 │ 1000000000
Pull Request
vnode fastest │ slowest │ median │ mean │ samples │ iters
╰─ vnode_clone 2.712 ns │ 3.025 ns │ 2.717 ns │ 2.721 ns │ 100 │ 1000000000
Benchmark - SSR
Yew Master
| Benchmark | Round | Min (ms) | Max (ms) | Mean (ms) | Standard Deviation |
|---|---|---|---|---|---|
| Baseline | 10 | 270.214 | 272.125 | 270.561 | 0.565 |
| Hello World | 10 | 470.772 | 482.678 | 478.514 | 3.807 |
| Function Router | 10 | 1550.812 | 1586.253 | 1568.509 | 10.931 |
| Concurrent Task | 10 | 1005.011 | 1006.697 | 1005.780 | 0.588 |
| Many Providers | 10 | 1031.203 | 1065.500 | 1049.301 | 11.537 |
Pull Request
| Benchmark | Round | Min (ms) | Max (ms) | Mean (ms) | Standard Deviation |
|---|---|---|---|---|---|
| Baseline | 10 | 281.405 | 281.765 | 281.542 | 0.116 |
| Hello World | 10 | 477.791 | 496.804 | 481.093 | 5.609 |
| Function Router | 10 | 1602.130 | 1617.413 | 1607.869 | 3.923 |
| Concurrent Task | 10 | 1004.972 | 1007.096 | 1006.098 | 0.810 |
| Many Providers | 10 | 1057.383 | 1115.522 | 1079.133 | 15.417 |
Size Comparison
| examples | master (KB) | pull request (KB) | diff (KB) | diff (%) |
|---|---|---|---|---|
| async_clock | 98.312 | 98.312 | 0 | 0.000% |
| boids | 166.434 | 166.434 | 0 | 0.000% |
| communication_child_to_parent | 91.440 | 91.440 | 0 | 0.000% |
| communication_grandchild_with_grandparent | 102.572 | 102.572 | 0 | 0.000% |
| communication_grandparent_to_grandchild | 98.954 | 98.954 | 0 | 0.000% |
| communication_parent_to_child | 88.811 | 88.811 | 0 | 0.000% |
| contexts | 103.940 | 103.943 | +0.003 | +0.003% |
| counter | 85.489 | 85.489 | 0 | 0.000% |
| counter_functional | 85.756 | 85.759 | +0.003 | +0.003% |
| dyn_create_destroy_apps | 88.647 | 88.647 | 0 | 0.000% |
| file_upload | 97.972 | 97.972 | 0 | 0.000% |
| function_delayed_input | 91.063 | 91.066 | +0.003 | +0.003% |
| function_memory_game | 169.376 | 169.381 | +0.005 | +0.003% |
| function_router | 328.280 | 328.325 | +0.045 | +0.014% |
| function_todomvc | 161.585 | 161.599 | +0.014 | +0.008% |
| futures | 236.928 | 236.928 | 0 | 0.000% |
| game_of_life | 103.849 | 103.849 | 0 | 0.000% |
| immutable | 192.977 | 192.979 | +0.003 | +0.002% |
| inner_html | 79.966 | 79.966 | 0 | 0.000% |
| js_callback | 107.514 | 107.517 | +0.003 | +0.003% |
| keyed_list | 179.134 | 179.134 | 0 | 0.000% |
| mount_point | 83.186 | 83.186 | 0 | 0.000% |
| nested_list | 112.699 | 112.699 | 0 | 0.000% |
| node_refs | 90.833 | 90.833 | 0 | 0.000% |
| password_strength | 1743.419 | 1743.419 | 0 | 0.000% |
| portals | 92.307 | 92.307 | 0 | 0.000% |
| router | 301.961 | 301.967 | +0.006 | +0.002% |
| suspense | 111.686 | 111.688 | +0.003 | +0.003% |
| timer | 88.241 | 88.241 | 0 | 0.000% |
| timer_functional | 95.934 | 95.938 | +0.005 | +0.005% |
| todomvc | 141.684 | 141.684 | 0 | 0.000% |
| two_apps | 85.341 | 85.341 | 0 | 0.000% |
| web_worker_fib | 132.882 | 132.896 | +0.014 | +0.010% |
| web_worker_prime | 184.158 | 184.185 | +0.026 | +0.014% |
| webgl | 82.524 | 82.524 | 0 | 0.000% |
✅ None of the examples has changed their size significantly.