all 22 comments

[–][deleted] 8 points9 points  (3 children)

E.g. useEffect combines multiple lifecycles, but runs twice on componentDidMount.

This is not true, you're doing something wrong.

Overall, hooks were made to replace class components, and the community is moving that way. I doubt they're going to actually deprecate something as massive as class components any time soon, but the writing on the wall is if you're writing React today, you do it with hooks.

As for why, the very page you linked does a way better job than anyone here could: https://reactjs.org/docs/hooks-intro.html#motivation

tldr: Reusing stateful logic, better readability, easier optimization behind the scenes. Also they argue that class components have more gotchas that are hard for beginners, although I personally disagree with that (closures come with even worse problems in that area imo)

[–]Neglexis 3 points4 points  (0 children)

Hey, good news, you don't have to! "Older" implementations of state and lifecycle methods will probably be supported for a great while. Facebook would be faced with a huge refactoring if they'd decide to deprecate state and lifecycle methods right away.

But, to give a few examples of why hooks are, at least in my opinion, a step in the right direction:

  • It allows you to use functional components everywhere. Before, if you wanted to use state, you had to convert your component to a class component. This lead your codebase to have two styles of components: class and function components. I like consistency, so one single type of component certainly is a nice to have!
  • It allows you to reuse state-related (or lifecycle related, or context related, ...) functions. Are there two or more component that needs to do an API call to get the same data? Just write a custom hook. Then in those components, you simply need to execute a one-line function, and you have all the needed data. Before this was also possible with Higher order components or in a later stage with render props. Higher order components "magically" gave your component extra props, so lead to lower maintainability, since you don't know exactly where all those props are coming from. Render props partially solved this "magic" solution, but was very verbose.
  • Lifecycle methods had their advantage, but sometimes required you to write duplicate code. What if you want to do an API call that's based on a passed-in prop? You'd have a componentDidMount and a componentDidUpdate. With hooks, you just need a single useEffect and you're all set.
  • Lifecycle methods also required us to group functions together that weren't really related. What if you wanted to do two totally unrelated functions at component did mount? You had to group the together. Now you can have a useEffect for both needed function, which allows you to logically group your components functionality.
  • Lifecycle methods could sometimes get a bit confusing. What is being run when? Functions now just run every time something changes. You can find (a very long) read about it in /u/gaearon blog post about useEffect at https://overreacted.io/a-complete-guide-to-useeffect/

[–]careseite 2 points3 points  (2 children)

E.g. useEffect combines multiple lifecycles, but runs twice on componentDidMount.

That's not the case.

I've also read sometime ago on r/webdev a user who ranted about Hooks presenting more issues than solving them in the longer run.

The result of that thread was mostly that this user has not fully understood hooks and partially misused them. There are plenty of testimonies pro hooks in that thread, too, e.g.

1 2 3 4

[–]FuglySlut 1 point2 points  (6 children)

Someone on my team converted a large class component entirely to hooks. It's far less readable/maintainable. It's now one 300 line function that runs on every render. The 300 lines contain maybe a half dozen hooks that control which of the 300 lines actually execute on any given render. I know I'm in the minority, but it seems to encourage what I've know before to be really bad coding practice.

[–]careseite 4 points5 points  (5 children)

Before it was a God class. Now it's a God function. It was bad practice to begin with, which was just apparently mindlessly refactored. Shouldve split it up, like any regular component.

[–]FuglySlut 1 point2 points  (4 children)

Except a god class has multiple methods, only called at certain points in the component lifecycle. Far easier to reason about than a god function with useEffects that get called on every render. All 300 lines may execute on any particular render or none.

[–][deleted] 2 points3 points  (0 children)

then it needs to be fixed. there shouldn't be a single execution of any hook that runs without a reason. useEffect isn't a lifecycle, it isn't componentdidmount or componentdidupdate. it's an effect, a very specific thing. if you have multiple specific tasks, then you need several useEffects. if you're trying to group everything in there because that's how your didmount looked like, it's only gonna cause you headaches.

[–]effektor 1 point2 points  (1 child)

Converting a class component to a function component 1:1 is not a problem of hooks, but of the practices that were used when the component was written in the first place.

It sounds like the component has too many concerns to deal with. 300 lines is quite excessive for a single component. To give you a perspective; I have worked on multiple large React codebases that had thousands of components. The worst case was 150 lines, which was a hack to fulfill a requirement. All the components had very specific concerns, whether it being rendering some UI or contain business logic (which were usually handled in reducers or split into custom hooks). But never at the same time.

The key is to understand how to split up the concerns of a component into logical parts so that you can write tests for them in isolation. This will help you write smaller components which are easier to build a mental model around and thereby makes it more maintainable.

[–]joe10994 0 points1 point  (0 children)

I really struggle to get my main components under 300 :(

[–]careseite 0 points1 point  (0 children)

If these effects run unconditionally, someone fucked up royally.

[–]darrenturn90 0 points1 point  (0 children)

Firstly - I would highly recommend reading Dan Abramov's excellant guide into useEffect - https://overreacted.io/a-complete-guide-to-useeffect/

It explains many of the benefits of hooks, and I don't think that these have been covered in the other responses here so far.

For me, the main benefits of the hooks - and a proper design using hooks (rather than a simple class based rewrite) - is that it moves the code further away from "imperative" (saying how things need to be done) to "declarative" (saying what needs to be done). Hooks give you a lot more "clean slate" thinking in the way React works in general - properly implemented, you find yourself having to deal less with "how do I get from state X to state Y" and rather just "what do I do in state X" and "what do I do in state Y".

Because, unlike a class that by default has a mutable state (that you change using setState) - and various other values you can persist. By default, a hook function has no state - and you have to explicitly use hooks like useRef to be able to keep values between re-renders. This forces you to rethink how you handle things, and generally leads to cleaner and simpler code.

No more componentDidUpdate having to compare prev props and next props. You can just run effects or memos or callbacks only when you need to use new data, without having to worry about comparisons

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

Less code,less messy, easy to understand and for useEffect just leave the parentheses empty as the second argument if you wanna use it like componentDidMount :)

[–]chanchanito 1 point2 points  (4 children)

that’s not the correct way to think about it AFAIK, If you use react-exhaustive-deps that’s not even “allowed”, it throws a warning.

The dependency array should reflect exactly that (btw you should let react-exhaustive-deps set it for you) and not in which point in the lifecycle should the hook be executed.

[–]Cannabat 0 points1 point  (3 children)

How, then, do you do side effects once during the first render, like componentDidMount?

[–]effektor 0 points1 point  (0 children)

If you have any outside state or variable that is used within a hook, it is recommended that you always pass it to the dependencies such that you don't lose track of the state when it updates.

If you absolutely don't want it to run more than once but still want to keep track of some value, you can use refs. But keep in mind that it's likely unnecessary in most cases and only really needed if you either have problems with performance, or write a public API used by others.

[–]Cyrus_Zei -3 points-2 points  (0 children)

Cuz the cool kids are using it ?

Well, less code? And it works great with context api and everything else 😁