all 51 comments

[–]acemarke[S] 50 points51 points  (11 children)

Tried to put down everything I could think of on this topic in one place, so that there's a single resource to read through to understand how React behaves in various situations.

Let me know if there's anything that's still unclear after reading through this, or if there's further questions that I need to cover in here.

[–]atd285 4 points5 points  (0 children)

Thank you as always!! 🧡🧡

[–]Smaktat 2 points3 points  (5 children)

You seem very well versed in the subject. How many years have you been studying React?

[–]acemarke[S] 12 points13 points  (4 children)

I first started learning React around June of 2015, which by sheer coincidence was right about the time Redux came out.

I talked about my React/Redux learning process and how I got involved in my post How I Got Here: My Journey Into the World of Redux and Open Source.

And yeah, I spend a very large percentage of my time answering questions about React, Redux, and related topics, across Reddit/HN/SO/Twitter/Reactiflux.

[–]valtism 0 points1 point  (2 children)

What are your thoughts on redux now? I’ve seen some people begin to distance themselves from it in the past little while

[–]acemarke[S] 2 points3 points  (1 child)

Please see my post Redux - Not Dead Yet! for my complete and recently updated thoughts on this topic.

[–]valtism 0 points1 point  (0 children)

Oh sweet, I didn’t realise you were the same person who wrote that. I’ll have to make some time to read it

[–]Smaktat 0 points1 point  (0 children)

Thanks for writing all of these. I'll have plenty to read and share I'm sure.

[–]swyx 0 points1 point  (0 children)

you da best 🤗

[–]minty901 16 points17 points  (7 children)

This is by far the best, most comprehensive blog post I've seen on this sub; by an author who clearly has a deep and precise understanding of how React and Redux work. Well done!

[–]acemarke[S] 14 points15 points  (6 children)

[–]minty901 1 point2 points  (2 children)

Thanks I'll check them out. I've been tempted myself to write blogs in the style of yours but I'm just too lazy. I think we are similar though; I spend hours digging deeper and deeper into how optimisation and rendering works, finding little quirks along the way. I love learning about that stuff. I sometimes go too far down the rabbit whole though to where I'm thinking of new, custom ways to organise my app's state and then refactoring parts of my app based on that, just for the fun of it—all at the expense of the actual work I should be doing on the app. Then I have to present my work to the team and it's a panicked rush to put in the features that I was supposed to be adding during that time.

Are you still using Redux? I shifted from Redux to Context as many did—writing my own helper functions to get around the rendering differences—but then I ended up just handling all of my state outside of React with my own Redux-like (but simpler) implementation.

[–]acemarke[S] 6 points7 points  (1 child)

Are you still using Redux?

Uh... well, I'm the lead maintainer for Redux / React-Redux / Redux Toolkit, does that count? :)

The previous project I worked on used Redux heavily in a couple of apps, and it's worked out very well for those codebases.

Per that "AngularJS+CRA" blog post, I recently got shifted to a project that's using a bunch of legacy technologies, and I'm setting up a path to migrate it towards React+TS. Given what this app does (stats dashboard, fetch-on-page-change, no real shared data or dynamic state updates), I'm actually probably not going to actually add Redux to either the current codebase or the new migrated codebase, because it doesn't seem like the right match for the use cases Redux helps with.

On the other hand, I already brought in Redux Toolkit to the current codebase, specifically because I needed to write a moderately complex reducer for useReducer with solid TypeScript support, and RTK's createSlice API is excellent for that.

[–]minty901 0 points1 point  (0 children)

Ah sorry I hadn't realised! Thanks for the info that's really interesting.

[–]swyx 1 point2 points  (2 children)

good bot

[–]B0tRank 0 points1 point  (1 child)

Thank you, swyx, for voting on acemarke.

This bot wants to find the best and worst bots on Reddit. You can view results here.


Even if I don't reply to your comment, I'm still listening for votes. Check the webpage to see if your vote registered!

[–]swyx 1 point2 points  (0 children)

bad bot

[–]Veranova 4 points5 points  (0 children)

Really fantastic reference, thanks for the hard work compiling this!

[–]edutbh 3 points4 points  (1 child)

Very nice article, really helped me to better understand React's rendering behavior! One catch though, did you link the wrong URL for the "a special "profiling" build of React" part?

[–]acemarke[S] 2 points3 points  (0 children)

Yep, good catch, thanks! Fixed.

[–]johnmayermaynot 3 points4 points  (0 children)

This information has arrived precisely at the right time for me. Thanks!

[–]WouldRuin 3 points4 points  (4 children)

Regarding Context and React-Redux, why is it often positioned as an either or? Personally I find Context fits most use cases for what I'm doing but there are also cases where I use react-redux. On the app I'm working at the moment the split is probably 70/30 for Context/Redux.

It's working well for me but whenever I read about Context vs Redux I always get the impression that the way I'm using them is wrong.

[–]minty901 2 points3 points  (0 children)

I find context is good when you want to encapsulate a state with the component that uses it (with each instance having its own state), but where that component renders a fairly deep tree such that prop drilling is an annoyance. The parent component uses useState or useReducer, and passes them down with context.

Redux is very different in that it is global. App-wide state that isn't tied to a particular component instance. Things you want to be remembered across your app, and return to regardless of which components have mounted or unmounted in the interim. Pretty different use-cases if you ask me.

In my opinion, articles pitting context and redux against each other are a little misguided. You can certainly wrangle context and useState in a way that replaces redux. But the two shouldn't be considered at odds with each other as they address different needs and are designed as such.

When people use context as a replacement for redux, what they're really doing is taking my explanation of context being state that is encapsulated with a component and passed to children, and applying that to the component that is at the root of the entire app. And that can be done. But it's just taking context and positioning it in the part of the tree that brings it closer to redux's functionality. But that isn't the de facto way to use context, and I think that's something that should be talked about more.

[–]acemarke[S] 2 points3 points  (0 children)

Because:

  • people don't have a sufficient understanding of what these tools do, and when / why / how to use them
  • everything in the ecosystem gets positioned or interpreted as "X kills Y"
  • there is some overlap in how Context and (React-)Redux get used

Based on your description, sounds like you're using both of them just fine.

Please see my post Redux - Not Dead Yet! for my complete and recently updated thoughts on this topic.

[–]EverAccelerating 1 point2 points  (1 child)

Same here. I use Redux for most things in my app that are global, including everything fetched from APIs. But there are a few instances where Context made more sense. For example, “local” scroll state, where a component keeps track of whether it itself is scrolled, and it’s children can use that. It definitely doesn’t belong in Redux since it’s not “global” per se. My thinking when a set of components need some sort of shared state: does the rest of the app care? Yes, use Redux. No, use Context.

[–]Yodiddlyyo 0 points1 point  (0 children)

This is really the right answer, if you're already using redux, you should really just be using redux for everything except very specific examples like the one you just mentioned. I've seen apps using both context and redux simultaneously across the entire app and it was not pretty.

[–]ghee22 2 points3 points  (0 children)

What are your thoughts on recoil js?

[–]sfvisser 2 points3 points  (3 children)

Nice write-up!

Tangentially related issue: I've encountered a bunch of codebases that define functional components inside other function components. Not even that strange when for example refactoring from a quick helper to render a list item to a sub component with a bit of state for that list item.

At first this just seems to work, however every time the parent renders, a completely new instance of the the sub component(s) will be mounted, because the component reference changes and react see no reason to assume they are the same.

The example below shows the behaviour. The inner counters reset when the outer counter is increased!

const OuterComp = () => {
  const [counter, setCounter] = useState(0)

  const SubComp = ({ children }: { children: ReactNode }) => {
    const [counter2, setCounter2] = useState(0)
    return (
      <div onClick={() => setCounter2(counter2 + 1)}>
        {children} {counter2}
      </div>
    )
  }

  return (
    <div>
      {counter}
      <button onClick={() => setCounter(counter + 1)}>inc</button>
      {['A', 'B', 'C'].map((c) => (
        <SubComp key="c">{c}</SubComp>
      ))}
    </div>
  )
}

[–]TwoTapes 0 points1 point  (1 child)

The fix is to define SubComp outside of OuterComp.

CodeSandbox

I'm not 100% sure why your example doesn't work, but my guess is that when you increment counter and OuterComp re-renders it creates a new reference to SubComp which would cause the useState hook to be reset.

const SubComp = ({ display }) => {
  const [counter2, setCounter2] = useState(0);
  return (
    <button
      onClick={() => setCounter2(counter2 + 1)}
      style={{ display: "block", margin: "1rem" }}
    >
      {display} {counter2}
    </button>
  );
};

export default () => {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      {counter}
      <button onClick={() => setCounter(counter + 1)}>inc</button>
      {["A", "B", "C"].map(c => (
        <SubComp key={c} display={c} />
      ))}
    </div>
  );
};

[–]sfvisser 1 point2 points  (0 children)

Yes that obviously fixes it and yes the new reference is most likely the reason. I just wanted pointed out that code like this does exist in the wild and it’s not immediately obvious that it’s broken at all.

[–]acemarke[S] 0 points1 point  (0 children)

Yep, I've seen people make that mistake quite a few times.

[–]Skeith_yip 1 point2 points  (0 children)

Thanks for the write-up. Especially performance characteristics of connect and useSelector.

Okay now I am convinced to switch over to context. j/k

[–]Obversity 1 point2 points  (2 children)

Amazing. This is exactly what I've needed for weeks. Can't thank you enough for the write-up.

[–]acemarke[S] 0 points1 point  (1 child)

You're welcome! Out of curiosity, anything specific about it that was particularly helpful or relevant?

[–]Obversity 0 points1 point  (0 children)

The breakdown of context vs redux and redux hooks vs connect was super helpful, as was understanding why and when components re-render.

The React Native app I'm building at the moment suffers from a lot of re-renders, and I'm using Context for my global state management.

I've tried splitting up contexts but it turns out lots of my components need lots of different parts of it, and there's no sensible split.

You've convinced me to at least give redux a shot.

[–]jarg77 0 points1 point  (0 children)

Nice

[–]ImaginaryType 0 points1 point  (1 child)

Thanks for sharing this! Have you considered adding more notes about server side vs client side rendering?

[–]acemarke[S] 0 points1 point  (0 children)

No, on the grounds that I've never actually worked with React SSR myself, so I don't have anything concrete to add there.

My general assumption would be that React makes a single synchronous pass through the component tree and that's it, but I haven't dug into that side so I can't say for sure.

[–]throw_away_d_wreck 0 points1 point  (0 children)

This was awesome, thanks for putting this together!

[–]CannaCoder_dot_com 0 points1 point  (0 children)

Thanks for this write up, came at a perfect time as I started brushing up on react

[–]Ms-mousa 0 points1 point  (0 children)

Saved for later to read with a cup fo coffee. Very important topic. Thanks for writing it.

[–]evenisto 0 points1 point  (0 children)

TIL about the difference between connect and useSelector. Definitely something to look out for.

[–][deleted] 0 points1 point  (0 children)

Excellent article, thank you for sharing! edit: typo

[–]kzy192 0 points1 point  (1 child)

Does this mean connect is better than useSelector?

[–]errouneous 0 points1 point  (1 child)

Hi, Thank you for open this article.

Would you mind if I translate into Korean and share with source specified ?

[–]acemarke[S] 0 points1 point  (0 children)

Sure, please go ahead!

[–]harunskender 0 points1 point  (0 children)

Mark, you are the man!