Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React 18 Enable Better ES6 Class Integration With Hooks #21787

Closed
mbostwick opened this issue Jul 2, 2021 · 9 comments
Closed

React 18 Enable Better ES6 Class Integration With Hooks #21787

mbostwick opened this issue Jul 2, 2021 · 9 comments
Labels
React 18 Bug reports, questions, and general feedback about React 18 Type: Discussion

Comments

@mbostwick
Copy link

mbostwick commented Jul 2, 2021

Problem Statement

One of the hardest part of working with javascript over the years has been keeping dealing with version support and change over time of third party code. It seems like react 18 is going to focus on performance and server side viewing framework. However, one of the big divides in many products, components and libraries is using ES6 Classes vs Purely Functional Components.

Timing Details

In past versions of react life cycle methods required use of classes. Modern javascript can use hoisting to make functional components faster to load. However, class components are also more consistent with larger market share of developers. The main motivation behind hooks in react though was to enable code to be branched and worked on more independently and by concurrent developers. Right now, functional components are seen as a place to make things faster but es class components are still considered ok.

Future Outlook

In the future hooks are likely to grow, developers who are use to older versions of react or who are use to OOP are likely to prefer class components, leading to lots of react code styles in lots of ways. This may make maintenance and community packages more likely to break as future versions of react try to take advantage of browsers IDL's and ECMA advancements. React 18 is also likely to add more of a footprint and more use cases. React native should also be considered, as more and more companies find value in javascript. Overall the community effect and the maintenance of react is likely to get harder and not easier, but the sooner es6 class components and functional components patterns can be well defined the more developers can adjust.

@mbostwick mbostwick added React 18 Bug reports, questions, and general feedback about React 18 Type: Discussion labels Jul 2, 2021
@mbostwick
Copy link
Author

mbostwick commented Jul 2, 2021

#11503 is also related to this..because of dynamic imports in ecma 2020, and its a solid example where not having a clear pattern to start with has lots of downstream effects. I think its a good example where just a little bit of planning would have made a breaking change less painful, but I can understand and in some ways agree to it being unrelated.

@bvaughn
Copy link
Contributor

bvaughn commented Jul 3, 2021

I don't think #11503 seems related to this.

We do not currently have any plans to stop supporting class components but our advice has generally been:

  1. Write new code using function components and hooks.
  2. Don't rewrite existing code that uses class components.

The class component APIs are old, have many deprecated methods, and often encourage unsupported things like side effects during render. While not perfect, the hooks APIs were designed to encourage safer coding practices by default. I suspect the majority of the core team's focus going forward will be on function components and hooks APIs.

The overall purpose of this issue seems unclear to me.

React 18 is also likely to add more of a footprint and more use cases.

I'm not sure what this means.

but the sooner es6 class components and functional components patterns can be well defined the more developers can adjust.

The APIs for both hooks and class components are documented and defined. Could you clarify what you mean by this?

@mbostwick
Copy link
Author

mbostwick commented Jul 4, 2021

ECMA 2020 has dynamic imports, private fields, and several other new features. The problem with the general recommendation is that in organizations the specific details matter. So when someone says classes will be supported and there is no value in re-writing code its likely the cost of changing patterns for internal requirements is just not there, so companies that have used classes will likely keep using those until there is a specific reason not to. I think react 18 is that reason, but truthfully, its not clear to me if changes in ECMA & typescript around classes will out pace react core teams work with hooks. For example look at decorators in typescipt: https://www.typescriptlang.org/docs/handbook/decorators.html

React 18 improves a lot of things when it comes to side effects, and rendering with lazy features. It does this with the work on hydration and the hooks api. Its clear to me that the focus of the react team will be on hooks and functions, hence the reason for this issue is to detail how hooks should/could be used in classes.

However, there's no real way for using hooks inside of classes: https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both . This means a component has to be re-written in order to use hooks. Its a bit ironic, that less then 2 major versions ago, classes were required for state components, but now to use the latest features using function components is required.

I think https://infinum.com/the-capsized-eight/how-to-use-react-hooks-in-class-components details how you can use 2 classes with hooks pretty well, but I think its possible to add a library to that uses decorators for integration with hooks, and make hooks a compile time consideration.

Shouldn't the react team deprecate es6 classes if the focus is going to be ignoring them, or why not support a mixin or private field way of integrating hooks to make side effects ? If the goal is to keep them running side by side, why not allow a clear path where they can be used ?

For the overall footprint questions, its a bit hard to detail exactly (i.e. that lots of people write react classes, but integrating hooks with classes, is all kinds of messy..) but I think adding some code examples to third party components helps in understanding exactly what I'm trying to get at:

@mbostwick
Copy link
Author

@bvaughn did that help clear up the confusion ? I'm happy to move this over to https://github.com/reactjs/rfcs if its clear what the approach should be for classes with hooks, my impression of 18 is that many libraries and components are going to have to be adjusted to work as expected, I understand that may be hasty. It just seems like a problem that would be easily understood, and would be talked about in the working group ? (but I totally get how getting consensus on this issue would be tricky..)

@markerikson
Copy link
Contributor

I'm going to echo Brian here and say that it's still very unclear what you're asking for here.

I'll give a slightly more blunt take than the React team probably will:

Classes are never going to be given access to hooks. It goes against everything they're trying to work on right now.

Classes aren't being truly "deprecated", because the React team isn't going to force the entire React ecosystem to suddenly rewrite most of their codebases ala Angular 1.x -> 2.x, and the components still run fine as-is.

However, all the new functionality the React team is working on is built around the assumption that you'll be using function components + hooks, like adding the new useTransition hook and Fast Refresh preserving state in hooks.

It's a carrot-and-stick situation. The stick is warnings that you need to stop using deprecated lifecycle methods. The carrot is the new features and functionality. They're trying to gently nudge the community towards moving to hooks over time, but without forcing everyone to change.

Beyond that, I don't think it's their job to go adding code examples to third party lib docs.

So, as far as I can tell, whatever you're asking for here (which is still very vague) is not going to happen.

@markerikson
Copy link
Contributor

I would think the core team would want to provide an upgrade path to the es6 classes rather then just forcing everyone to re-write them over time..

The React team has done a great job of providing codemods for syntax changes like componentWillReceiveProps -> UNSAFE_componentWillReceiveProps. But, those are fairly straightforward mechanical transformations.

There's obvious parallels between class components and function components, but it's exactly the kind of nuance and potential bugs you're pointing to that makes it hard if not impossible to actually come up with a codemod that can safely and consistently convert class components to functions.

That's why the recommendation is:

  • Leave class components alone for now
  • Write new code as function components
  • As needed or you really have time, hand-convert class components to function components

The other thing to keep in mind is that use of ES6 classes as React components has never been about fully using the class syntax itself. It was about standardizing on a common syntax in general, as opposed to the React team maintaining their own class-like implementation. Use of things like decorators and class inheritance has always been discouraged in relation to class components, and it's not like TC39 adding new class-related syntax is going to have any impact or relevance here. Besides, class components are a facade over the real data inside of React's internal data structures anyway.

@mbostwick
Copy link
Author

...Please forgive, the mistake, used the wrong username to post to this ticket..reposting under the proper user name..

Given your relationship to redux, and your sentiment likely being shared, I think its clear people should avoid es6 classes in the final export at this point (also as to how the core team is thinking about change over time in ECMA, it seems its not being considered or at the least not being talked about ?)...as to exactly what I was looking for in this issue, it was to get a better a more informed judgment on an internal conversation and some conversations I've had with other react developers on when to rewrite a react component to functional component (as this can be a tricky call sometimes).. I'm certainly not looking for code examples, the examples I provided were ones that didn't look like they were updated for react 18 but seemed like they might want to be, or provided some other perceived value (and in more then 1 case had been touched by react core team members).

As to what I was suggesting could be done (i.e. don't just bring problems 😉), is I was thinking about maybe writing a webpack plugin to convert something like this date-editor.js to be written like

class DateEditor extends Component {
   @useRef(null)
   #dateRef;
  
    @props('defaultValue')
    #dateValue(defaultValue){
        this.date.valueAsDate = new Date(defaultValue);
    }
    
   @props('className')
   #editorClass(className){
        //cs is a libary being used by the component
        cs('form-control editor edit-date', className);
   }

   @useEffect()
   @props('didMount')
   #triggerFocusAndIndicateMount(didMount){
        this.date.focus();
        if (didMount) didMount(); 
   }
  
  @render()
  display(){
      <input
        ref={#dateRef }
        type="date"
        className={ #editorClass }
      />
  }
}

which then webpack plugin could transform that to

export DateEditor(props) => {
   const dateRef = useRef(null);
   const dateValue = new Date(props.defaultValue)
   const editorClass = cs('form-control editor edit-date', props.className);
   
    useEffect( () => {
       this.date.focus();
        if (didMount) didMount(); 
    }, [props.counter])

  return (
   <input
        ref={dateRef }
        type="date"
        className={ editorClass }
      />
  );
  
}

An astute observer will note this rewrite would create a bug in regards to getValue, there is a solution but its omitted from this example (for lots of reasons). However, the main goal was just to show one approach, not that I'm super keen on the approach driven from webpack transformations.. but it just seems like doing it would allow es6 components to continue and use class structures. This approach would avoid needing to have anything to do with the core team, but I would think the core team would want to provide an upgrade path to the es6 classes rather then just forcing everyone to re-write them over time..

As that means you get the value of hooks, and can continue to use es6 classes..but doing that can be done with out any interaction from the core team. There's lot of ways though that hooks could be hooked into classes, and if you check out the last highlights from ECMA you can see classes are likely going to get more powerful..

Hopefully what I was getting as is more clear, and I get it, if you guys want to close this, just seems is for sure a pain point for me and some other people I've talked with + I saw a few other issues where this was kind of talked about..

@mbostwick
Copy link
Author

The problem is most features that react 18 adds makes it so you don't get the performance boosts with out the usage of hooks..i.e. I don't actually think there's a huge value converting DateEditor, but something like react-virtualized and google maps would actually benefit from certain things using hooks, but who in there right mind would want to get near that kind of pr, hence the reason why allowing hooks to integrate with classes would allow for sections of code to be moved into hooks, and performance boosts could be targeted and incremental. I mean maybe jscodeshift is the better solution...

I don't know about your experience, but a lot of people just prefer copying and pasting, and coding something the same way, so when you get 2 approaches in a project, the drift and scope of pr's is not really a small or easy task..Allowing for hooks to play nice with class components means, people can add new things with hooks, and in time phase out things..

"We strongly recommend against creating your own base component classes. "(and very sensible..) has always been followed in anything I've seen or worked with, but something like adding a new ReactBaseClasses that supports the core teams hooks seems reasonable ?

The recommendation though just doesn't seems like its putting off the decision for how to handle class components, because time is always a resource, which in an enterprise to be spent only when necessary. Which means a lot of things aren't likely to get hooks, and it just leads to the community getting more fragmented..I mean the recommendation could be hoc should use class components, and hooks should be used in all other cases, and make the patterns more clear..

@mbostwick
Copy link
Author

Closing this though, since it seems like, its a situation where it is what it is..feel free to open it up though(if you have permissions) and think there's value in doing so.. I think using 18 is going to be really hard given the way es6 classes are, and it pains me to move things to a new style, when classes were the pattern not that long ago..but that just feels like I'm cranky about change.. hopefully the react working group though will consider talking about this, and solving it in time..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
React 18 Bug reports, questions, and general feedback about React 18 Type: Discussion
Projects
None yet
Development

No branches or pull requests

3 participants