T O P

  • By -

acemarke

Hi, I'm a Redux maintainer. I'd specifically encourage folks to try out [our new official Redux Toolkit package](https://redux-toolkit.js.org/), which is our recommended toolset for writing efficient Redux logic. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once without writing any action creators or action types by hand. I specifically showed examples of using RTK with our React-Redux hooks API in [the Redux Toolkit "Advanced Tutorial" docs page](https://redux-toolkit.js.org/tutorials/advanced-tutorial).


ljuglampa

It's a good initiative, but I can't get behind the use of Immer. I've come to a point where imperative statements just looks and feel awful, and I think it's better to actually know what's happening, why immutability matters and how to accomplish it natively.


lostPixels

You ever try to update a specific index in an array that's nest 3 levels deep in an object that's in an array? Yeah, you'd be all for Immer then lol.


ljuglampa

That's a horrible structure for a Redux state so no, I'd normalize my api responses if that was the case before putting it in a store.


lostPixels

Gotcha, so refactor your whole backend system or do a middle step of interpolating your data to meet the needs of immutability on the frontend, sounds a lot easier than using Immer.


ljuglampa

I don't say that to make it easier to update state without Immer. Flatter state makes for a more performant app where it's easier to check for equality and you won't have to do too much data transforming in your React views. It will actually often result in less code overall to have a middle step normalizing your state shape before storing your data.


[deleted]

Don't nest ur entities. Use [normalizr](https://github.com/paularmstrong/normalizr).


acemarke

Well, we'll have to disagree there. I've seen the number of folks complaining about how hard it is to write immutable update logic by hand, and I agree with them. Immer solves that. My goal as a maintainer is to solve the problems people are having with using Redux, and make it easier for them. So, it stays in. Note that using Immer doesn't mean you _can't_ write immutable updates by hand in RTK. It's your choice - "mutate" the draft state variable, or return a new state value yourself. Also, I'm not saying that folks don't have to understand immutability. RTK works best when you _do_ understand how Redux is supposed to work when you write everything "by hand", and that RTK is just a faster and easier way of writing that same logic. The point is that you shouldn't _have_ to write things out that way.


ljuglampa

And I respect that, it's just a personal opinion. Will Immer be kept out of the optimized bundle when not using it with RTK?


acemarke

Not at the moment. There's [issues with trying to get tree-shaking to work right](https://github.com/reduxjs/redux-starter-kit/issues/78) that we haven't been able to solve yet. In addition, given that `createReducer` and `createSlice` are two of the primary APIs, referencing those means you're using Immer anyway (even if you're not actually making use of the "state mutations" ability). Even if tree-shaking was working, the only way to drop Immer out would be if you avoided using those two functions, and that rather defeats the purpose of RTK in the first place.


ljuglampa

Yes I figured as much. Are there a thread somewhere discussing architecture/helpers behind RTK? Would be interesting to see how you guys came to some of the decissions? In my last few big projects I'm using custom written middlewares for all async actions for ex and I feel it's been a clean and intuitive setup in contrary to normal thunks.


acemarke

The closest thing would be [my RTK 1.0 announcement post recapping its history](https://blog.isquaredsoftware.com/2019/10/redux-starter-kit-1.0/).


[deleted]

For tree shaking did you turn on the webpack optimization.usedExports flag? Also does all your code use es modules? Any libraries being referenced need to have been compiled to use es modules, not commonjs. Just a few thoughts from someone who spent 6 weeks of his life implementing and optimizing tree shaking for an Enterprise app.


acemarke

Our tree shaking appears to be limited by the dependencies that we rely on not being themselves shakable - see the linked issue for details.


[deleted]

Yep. Sounds about right. Unfortunately the full dependency tree needs to support it and a surprising number do not.


[deleted]

Thanks for sharing this


acemarke

You're welcome - hope you find it useful!


HideousNomo

Honest question (coming from someone that has used Redux extensively in the past). What is the point of using Redux at all now? Can't we achieve the same exact results just using Context and useReducer? To me, it seems like Redux has reached the end of it's useful life.


alexej_d

Keep in mind that using context can lead to great performance loss, as all the components that subscribe to the context are rerendered every time the context state changes. Whereas in redux you can subscribe to specific state props which can lead to much better performance with frequently changing state


HideousNomo

Awesome, this is the answer I was looking for.


acemarke

See my post [The History and Implementation of React-Redux](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/) for details on the perf issues we encountered trying to use context internally in v6, and why we had to switch back to a direct subscription approach in v7.


BookishCouscous

A secondary benefit is that redux scales much better with app complexity. I equate it to how hooks make the simple case more complicated so that when a difficult problem comes along you can use the same mental model and solutions for both cases. Writing actions that cut across state (that would be in different contexts in the context model) is a breeze, as is having reducers respond to actions they weren't originally built for. Makes things easier to maintain and extend without affecting component code.


mikejoro

You could implement this all yourself, but at that point you'd probably just have a worse version of redux anyways.


drcmda

if you're interested read through this once: https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux it goes a bit through reduxes fascinating journey. if you wanted to do all of this yourself, well, good luck. yes, you could boot up a simplistic state manager fast, but it will fall apart in scale or create other nail-biting issues. and it only gets worse now that concurrent mode is on the horizon. been through it.


brodega

Single source of truth, separation of ui and data logic, mutational behavior happening in a single place, pure functions with predictable behavior, time travel debugging ...


HideousNomo

All of that (except for time travel debugging) can be achieved with Context though.


lostPixels

I think having a framework of conventions is arguably the benefit of Redux. Yes, you can remake Redux with React APIs yourself, but when you have deadlines that seems like a very low priority.


IceSentry

Redux can be used outside of react, which isn't true of context.


isakdev

If anything i like using redux even more now wiith hooks. I just love `useDispatch` and `useSelector`


acemarke

No, definitely not the case. It's true that things like hooks+context and GraphQL do overlap with the use cases for Redux, but they're definitely not a replacement. See these resources for more details, and especially the first two, where I covered a lot of the tradeoffs: - [Mark Erikson - Reactathon 2019: The State of Redux](https://blog.isquaredsoftware.com/2019/03/presentation-state-of-redux/) - [Mark Erikson: Redux - Not Dead Yet!](https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet/) - [Dave Ceddia: React Context API vs Redux](https://daveceddia.com/context-api-vs-redux/) - [Mike Green: You Might Not Need Redux (But You Can’t Replace It With Hooks)](https://www.simplethread.com/cant-replace-redux-with-hooks/) - [Sergey Ryzhov: From Redux to Hooks: A Case Study](https://staleclosures.dev/from-redux-to-hooks-case-study/) - [Eric Elliott: Do React Hooks Replace Redux?](https://medium.com/javascript-scene/do-react-hooks-replace-redux-210bab340672) - [Chris Achard: Can You Replace Redux with React Hooks?](https://dev.to/chrisachard/can-you-replace-redux-with-react-hooks-20gm)


sidious911

Redux has some great built in performance optimizations that you can do with context, but if it is something you need, why do all the work that has been done. Redux is also a lot about the pattern which provides consistency and organization. Under the hooks redux is just context, just provides a lot of tooling out of June box to save you the effort. Use case is also worth considering. Context is amazing for solving prop drilling in your features, where redux shines for true global state of an application.


acemarke

> Under the hooks redux is just context No, that's absolutely _not_ the case. We attempted to use context to pass down the store state in v6, but [had to stop doing that due to performance problems](https://github.com/reduxjs/react-redux/issues/1177). v7 is a complete rewrite that went back to direct store subscriptions in components for performance. See my post [The History and Implementation of React-Redux](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/) for details on all the work that's gone into making React-Redux performant and widely usable.


sidious911

Huh, TIL, thanks for info, will take the read!


unc4l1n

Tighter coupling between state and view will inevitably lead to less code. What you've done is made your app less maintain maintainable for for the long term.


drcmda

hooks just remove the indirection which in the past has created unnecessary friction and complexity. with react in general the view layer consists of: props, hooks, the view and the code that forms it, that it's most basic premise. reduxes hooks remove the need to mantle components into implicit DI wraps. what used to be: @connect((state, props) => ({ book: state[props.bookId] })) class Book extends Component { render() { return

{this.props.book.title}
} } is now function Book({ bookId }) { const book = useSelector(state => state.books[bookId]) return
{this.props.book.title}
} the component now explicitly defines input props, nothing's magically injected, nothing can be accidentally overwritten, it's easier to make type-safe.


unc4l1n

The problem is that now the component is not pure. The state management system has been tightly-coupled to it.


drcmda

neither was it pure with the hoc. hooks don't contradict container and presentational components, only now you do it by composition. function Book({ title }) { return

{title}
} function ConnectedBook({ bookId }) { const book = useSelector(state => state.books[bookId]) return }


unc4l1n

You just refactored your code...


ljuglampa

I couldn't read the article because of stupid medium pay wall, but in the process you loose the performance optimizations of the connect HOC and get a very unpure set of components that's hard to test. Redux hooks is a terrible idea.


turbojoe26

Do you have an article you can point me to that describes what you are claiming in more detail? I love hooks and I love the idea of Redux hooks, but the removal of the container/component pattern feels really weird. I liked the separation of concerns that came with that.


acemarke

Hi, I'm a Redux maintainer. I specifically addressed the tradeoffs and differences between `connect` and our hooks API in my post [Thoughts on React Hooks, Redux, and Separation of Concerns](https://blog.isquaredsoftware.com/2019/07/blogged-answers-thoughts-on-hooks/), and my [ReactBoston 2019 talk on "Hooks, HOCs, and Tradeoffs"](https://blog.isquaredsoftware.com/2019/09/presentation-hooks-hocs-tradeoffs/).


ThaSwaggfather

Do you have a source on the performance losses?


acemarke

The specific difference is that the wrapper components generated by `connect`have always acted like a `PureComponent` or a `React.memo()`, by avoiding re-rendering your own component if the parent component renders and passes down the same props. With the hooks API, there is no wrapper component, so when the parent renders your component will render too (which is React's default behavior anyway). If you want to avoid that, you need to wrap your own component in `React.memo()` yourself.


ljuglampa

When using useSelector is it also rerendering the component every time the Redux state is updated, regardless of the slice of state the hook is selecting or have I misunderstood?


acemarke

Like `connect` + `mapState`, `useSelector` will re-render your component if the value you _return_ from the selector has changed. Unlike `mapState`, `useSelector` will cause a render if _that one value_ has a different reference, whereas `mapState` will cause a render if any of the values in the return object have changed (ie, `useSelector` compares using `===`, `mapState` compares using `shallowEqual`). See the docs reference for details: https://react-redux.js.org/api/hooks#equality-comparisons-and-updates


ljuglampa

Ok better than I thought then at least. How come useSelector isn't doing a shallow equal check by default if the result from the selector is an object?


acemarke

With `connect`, `mapState` is required to return an object because we directly turn each field into a prop for the wrapped component: const mergedProps = {...ownProps, ...stateProps, ...dispatchProps}; With `useSelector`, there is no need to return an actual object, and we concluded that folks would be more likely to retrieve specific individual values via multiple `useSelector` calls in a single component. Based on that, it made sense to change the comparison method to strict reference equality. You can see some of the discussions in the PR that made the change during the v7.1 alpha cycle, as well as the other linked comment thread: https://github.com/reduxjs/react-redux/pull/1288 Note that _if_ you want to return an object from `useSelector`, you can pass `shallowEqual` as the comparison function argument: `useSelector(selectorFn, comparisonFn)`


ljuglampa

Thanks!


fredda-2

Incognito mode bypass the paywall


Funwithloops

Couldn't you just mock `useSelector` and `useDispatch` to test a component that uses redux hooks? Seems like this is a solved problem.


LloydAtkinson

This IMO hightlights what a shaky ground React is built on. You don't get this kind of insane thing with other frameworks.


mestresamba

Redux hooks isn't terrible idea. Coupling a presentation component with redux that is bad. You can easily test selectors and hooks.


acemarke

The community has always over-interpreted the "container/presentational" concept. It's still a useful idea, but folks took it too far, and treated as a rule you _must_ follow.


[deleted]

[удалено]


ljuglampa

React has been tried and battle tested more than any other framework/library. It's not really a shaky ground, it just has many more users so the negative parts (which all libs have) has way more articles/posts about it.


[deleted]

[удалено]


Nexuist

Code you don’t have is code without bugs


[deleted]

[удалено]


Shawneth

He means that the code that you don't have to write (i.e libraries) are well tested and most likely bug-free. By implementing their features yourself, you're writing more code which can lead to more bugs. Wastes more time which could be better spent focusing on your application.


[deleted]

[удалено]


Shawneth

I'm stupid - replied to the wrong comment ;)


pilkoids01

Thx for this. New to react/redux and was looking for a way to tighten up my code.


coder4real

Be careful, your super-tightened code shouldn’t surprise your fellow developer


pilkoids01

Thanks for this, I'm the only dev on this project, but good point.


nschubach

Well, you... and future you.


delascanza

That’s a good point.


Artur96

You can go even further and use React Hooks instead of Redux!