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

RFC: React Hooks #68

Merged
merged 3 commits into from Nov 21, 2018

Conversation

Projects
None yet
@sebmarkbage
Collaborator

sebmarkbage commented Oct 25, 2018

In this RFC, we propose introducing Hooks to React. See the RFC and the documentation for more details.

https://reactjs.org/docs/hooks-overview.html

View formatted RFC

(The talk video has now been published too.)


Nov 16 Update by @sophiebits:

See @sebmarkbage’s comment #68 (comment) for a detailed response to the preceding comments that covers various tradeoffs and articulates why we’ve landed on the current design. If you have something to say, please check that it is not answered there already.

RFC: React Hooks
In this RFC, we propose introducing *Hooks* to React. See the RFC and the documentation for more details.

https://reactjs.org/docs/hooks-overview.html
@grabbou

This comment has been minimized.

grabbou commented Oct 25, 2018

Question about persisting values of useState - is the preferred way to create a custom usePersistedState hook that calls into storage mechanism?

I was wondering if there was a way to batch the write operations in environments such as React Native and how did you approach that problem at Facebook during your initial adoption?

@pcmaffey

This comment has been minimized.

pcmaffey commented Oct 25, 2018

Is this the right place for feedback? First of all - amazing work. I love where this is going with composability. My big question is about performance of Hooks.

Are all these function hooks redefined and recalled on every render?

@pheuter

This comment has been minimized.

pheuter commented Oct 25, 2018

Just watched the React Conf keynote, awesome presentation, super excited to give hooks a shot!

Not sure if this is the right place to ask but was wondering how function components that make use of hooks affect server-side rendering. In useEffects() for example, will errors be thrown when accessing browser apis like window during ssr?

@pheuter

This comment has been minimized.

pheuter commented Oct 25, 2018

@pcmaffey Looks like this FAQ entry addresses your performance concern.

@JoshuaKGoldberg

This comment has been minimized.

JoshuaKGoldberg commented Oct 25, 2018

Very exciting, this looks awesome!

Questioning the tuple return type:

const [count, setCount] = useState(0);

Why an array tuple instead of an object containing count and setCount members? Is this for performance (and if so, do we have numbers to show browsers or Node behaving strongly differently)? It feels like leaving the returned object open to adding more fields might make it easier to remain backwards compatible in the future, similar to how APIs often end up taking in single objects as a parameter rather than long lists of parameters.

const { value, set } = useState(0);

Edit: yes, per @jaredLunde's comment, the properties would need constant names.

@jaredLunde

This comment has been minimized.

jaredLunde commented Oct 25, 2018

@JoshuaKGoldberg How would you customize those object properties? Array is the cleanest way it works with custom naming.

@alexeyraspopov

This comment has been minimized.

alexeyraspopov commented Oct 25, 2018

Why an array tuple instead of an object containing count and setCount members?

You can assign names you need. Object destructuring requires you to put specific keys. You may end up having something like

let { state: name, setState: setName } = useState('Name');
let { state: surname, setState: setSurname } = useState('Surname');

Which is not the case with tuples.

@jamesplease

This comment has been minimized.

jamesplease commented Oct 25, 2018

I'm still familiarizing myself with this API, but I'm optimistic about these changes. Great work, React team!

My main concern with hooks is related to the learning curve. There are some APIs introduced that new (and seasoned) developers may not immediately pick up on.

One in particular that stands out the most to me is useEffect(myEffect, []). It's not very expressive. For those who haven't read the docs, what do you think this does? I'd be surprised if someone could guess.

For those who are still familiarizing themselves with the API, it calls the effect only on mounting and unmounting, and not on updates (docs here).

I know that this proposal introduces a lot of new functions, but it might be worthwhile adding another one that functions the same as useEffect(myEffect, []). As for the name? I'm not sure. Perhaps something like useMountEffect()?

@eps1lon

This comment has been minimized.

eps1lon commented Oct 25, 2018

It is a very early time for Hooks, so some integrations like DevTools support or Flow/TypeScript typings may not be ready yet.

Will I be able to inspect what hooks a component is using?

Having an object with named properties as state made it easy to see what's what. I guess this will be hard with the new useState?

@benjamn

This comment has been minimized.

benjamn commented Oct 25, 2018

@eps1lon According to the FAQ, useState is backed by a list of anonymous memory cells:

There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.

So if you ever find yourself trying to debug the underlying state, you're probably going to be working with a linked list of unnamed values. To figure out which memory cell is which, you'll have to refer back to the order of your useState calls.

Full disclosure: I'm planning to open an issue today to discuss a system for providing meaningful names for your useState calls, which should significantly relax the current usage restrictions, and eliminate the need for a linter to enforce those rules.

@spicydonuts

This comment has been minimized.

spicydonuts commented Oct 25, 2018

Positional effects (using any of these use* functions is an effect, even just to read state) worry me. It seems easy to get wrong, and doing so might not present until production. For the same reason it also seems like it'll be extremely difficult to introduce to beginners -- "the magic function works here but not there".

Using Symbols instead of positional keys would make it easier to understand and harder to get wrong:

const $count = Symbol("count");

function MyComponent() {
  const [count, setCount] = useState($count, 0);
  ...
}

Edit for clarification: I don't mean to say the Symbol would be a global key -- it would still use the current context (the Fiber, I assume) as the primary key and the Symbol as the secondary key (where the effectful call order is currently being used).

Another edit for followup: After playing with hooks a bit more I don't feel as strongly about this as I thought I would. It's not perfect, but you could also pass the wrong Symbol in this alternative as well. Also seeing the eslint plugin catching on relieves the worry I had a bit too.

@adamhaile

This comment has been minimized.

adamhaile commented Oct 25, 2018

Just curious, are React hooks at all inspired by S and Surplus? There are similarities down to even the names and terminology used. S has S.effect() and S.cleanup(), and useState() is analogous to S's S.data().

For instance, here's the hooks example in Surplus:

function Example() {
  const count = S.data(0); // S.data instead of React.useState

  S.effect(() => { // S.effect instead of React.useEffect
    document.title = `You clicked ${count()} times`;
  });

  return (
    <div>
      <p>You clicked {count()} times</p>
      <button onClick={() => count(count() + 1)}>
        Click me
      </button>
    </div>
  );
}

In fact, I used almost this exact same example in the Surplus FAQ to explain how functional components could have state -- the very same issue hooks are solving for React.

I'm the author of S and Surplus, and reading the hook docs gave me a strong case of déjà vu! The similarities may be coincidental -- we're probably reading the same upstream sources. But if there was any influence, I thought that would be cool :).

@alexeyraspopov

This comment has been minimized.

alexeyraspopov commented Oct 25, 2018

@alqamabinsadiq, sorry if my comment was somehow confusing. This is not how the hook intended to be used, I was making an example of additional effort required for using object destructuring. This is how the correct code looks like:

let [name, setName] = useState('Name');
let [surname, setSurname] = useState('Surname');

Less effort to write, less effort to read.

@sophiebits

This comment has been minimized.

Member

sophiebits commented Oct 25, 2018

@adamhaile To my knowledge we weren’t influenced at all by Surplus (this is my first time seeing it). It’s true the “effect” name is similar, but both names are descriptive, so I guess it shouldn’t be too surprising that the names overlap.

@sebmarkbage

This comment has been minimized.

Collaborator

sebmarkbage commented Oct 25, 2018

@benjamn @spicydonuts If you decide to publish a follow up RFC with named hooks which doesn’t have the conditionality constraints, then be sure to cover other pitfalls that you may also get. In the current proposal there is clearly one thing that will unexpectedly break when you don’t have names. However there are other things that might become confusing too. Such as that an effect doesn’t cleanup and refire when it’s in a conditional. So even with names you might find it best practice to avoid conditionals.

@spicydonuts

This comment has been minimized.

spicydonuts commented Oct 25, 2018

I see what you mean. Hmm.. that's unfortunate 🤔

@rudolfolah

This comment has been minimized.

rudolfolah commented Oct 25, 2018

I like the idea, but I think the naming doesn't really reflect what's going on here. The term hook in other contexts, such as web hooks or Emacs, implies that something happens at a particular point in time and calls out to some other functions/methods to make it happen.

In the words of the Github docs for Web hooks:

Webhooks allow you to build or set up GitHub Apps which subscribe to certain events on GitHub.com. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL.

For example web hooks are called whenever Github detects a branch has been created. They're called in Emacs whenever a file is loaded or a particular mode (for syntax highlighting + shortcuts) is activated.

In React, the only hooks I can think of at the moment are the componentDidMount and componentWillUnmount functions, since they're part of a lifecycle and call out to some other function (as part of being overridden in a Component class)

From the React Hooks documentation it looks like these are managers:

  • useState manages and coordinates state
  • useEffect manages and supervises side-effects
  • useContext manages context

Other options for naming:

  • manager
  • supervisor
  • coordinator
  • handler

I'm sure there are other, better names other than "hook" which can be used.

Also, the naming of the methods don't need to change since none of them seem to have the word "hook" in them, the naming change would just affect the rest of the documentation/examples.

@leebyron

This comment has been minimized.

leebyron commented Oct 25, 2018

Loving this new API, but I have one primary concern - where the hooks come from.

I'm concerned that these hook functions are provided by the top level react module and imply (or require) global state to work. Ideally, hooks and effects functions could be provided to the functional component when it's executed.

So while this example as illustrated by Dan requires from the module:

const { useContext } = require('react')
const SomeContext = require('./SomeContext)

function Example({ someProp }) {
  const contextValue = useContext(SomeContext)
  return <div>{someProp}{contextValue}</div>
}

Was it considered to pass the hooks into the functional component?

Perhaps looking something like:

const SomeContext = require('./SomeContext)

function Example({ someProp }, hooks) {
  const contextValue = hooks.useContext(SomeContext)
  return <div>{someProp}{contextValue}</div>
}

This means functional components which use hooks are (Props, Hooks) => ReactNode. While this is still not "pure" because hooks have effects and are not pure, at least the renderer could provide the appropriate hooks per call-site instead of relying on global state. I could imagine that the Fiber renderer might have different implementation of hooks than some potential future renderer, a server-side renderer, a test renderer with mocked effects, etc.

Thoughts?

@KARTIK01

This comment has been minimized.

KARTIK01 commented Oct 25, 2018

for new react developers, useState hooks will be a magic.

@ntucker

This comment has been minimized.

ntucker commented Oct 25, 2018

How does this interact with React.memo()? Does it just shallow check props and ignore state and context. If not, how does it know what state and context to check?

@alexeyraspopov

This comment has been minimized.

alexeyraspopov commented Oct 25, 2018

I'm concerned that these hook functions are provided by the top level react module and imply (or require) global state to work. Ideally, hooks and effects functions could be provided to the functional component when it's executed.

@leebyron, I think, the way how it works, is by using an incapsulated access to the current rendering fiber that holds the state and effects. The API may look like something "global" happens under the hood, but it is still scoped to the specific fiber that reflects the component that is rendering at the moment.

@benjamn

This comment has been minimized.

benjamn commented Oct 25, 2018

@leebyron Alternatively, functional components could be invoked by the React internals with a meaningful this object, containing any number of useful methods.

@david

This comment has been minimized.

david commented Oct 25, 2018

What's the advantage of this approach over a HOC-based one such as the one used, for example, in recompose?

@sebmarkbage

This comment has been minimized.

Collaborator

sebmarkbage commented Oct 25, 2018

@leebyron Conceptually they’re algebraic effects. It just happens that the engine (a pointer to the current “handler”) lives in the react package. Note that the actual implementation doesn’t live in the react-package. So anyone can implement their own dispatcher. Even inside a react render. If this is a popular pattern you could just have a general purpose pattern for this like “dispatcher” package or something.

Just like context there doesn’t have to be one. In multi-threaded environments there can be several.

We considered passing it but then you would have to pass it through multiple layers of indirection. We already know this to be a pain (which is why we have context to begin with) and for something as light weight as custom hooks it is even more so.

@jaredLunde

This comment has been minimized.

jaredLunde commented Oct 25, 2018

@david acdlite/recompose@7867de6

@sebmarkbage

This comment has been minimized.

Collaborator

sebmarkbage commented Oct 25, 2018

@benjamn The this was actually in the first idea and later dropped because it added weirdness and got even weirder when composed with custom hooks. Since they would need to change to a different variable or use .call.

@probablyup

This comment has been minimized.

probablyup commented Oct 25, 2018

Are there any concerns about the interaction of transpilers with state hooks? The order of function calls could conceivably be rearranged by a transpiler or minifier in a way that differs between an SSR and client-side bundle.

@ChibiBlasphem

This comment has been minimized.

ChibiBlasphem commented Dec 4, 2018

@rob2d react is batching calls to setState

@AlexGalays

This comment has been minimized.

AlexGalays commented Dec 4, 2018

@rob2d react is batching calls to setState

Just to clarify, it does batch in some occasions, like when inside an event handler. In fact, that's like the only place it batches anything, unless they changed the architecture a lot.

Meaning, this code will rerender TWO times:

const [numA, setNumA] = useState(1)
const [numB, setNumB] = useState(2)

useEffect(() => {

   // Simulate some external subscription
   setTimeout(() => {
     setNumA(10)
     setNumB(20)
   }, 1000)

 }, [])

So the original question was very much valid.
The advice is very simple: group the state pieces that are usually updated together, else it's safe to split them (or not, if you prefer not to).

@rob2d

This comment has been minimized.

rob2d commented Dec 4, 2018

@AlexGalays thanks for the input. If this is still the case in v16, seems using a shared state object is a very sane approach for me at the moment. I think as long as you realize that calling a setter will cause a value to be replaced (hence triggering a re-render), then you can just perform a shallow diff with a new state or call for changes opportunistically.

There's a bit of unnecessary mental energy if you have to memorize the interfaces in long term large codebase for nuances on how pieces of state are grouped; state in components is pretty terrible on this front as is (though I am sure it is due more to abuse/bad design on codebases I have had to work on).

I do prefer this new hooks approach to writing things, and realize that's an entirely opinionated thing though!

@lasthrun

This comment has been minimized.

lasthrun commented Dec 4, 2018

I have noticed in almost all examples, state is being broken down into very discrete primitives. To avoid multiple re-renders due to multiple setXXX calls and also cut down on boilerplate/noise, would it be considered bad practice to decompose general state down from objects (the way we do in normal React components)?

Example:

let [{ name, age }, setState] = useState({ name : 'Bob', age : 41 });

Hopefully not O/T. I see that you can technically use objects in the doc, but since this is a new RFC, it has been hard to find the info elsewhere on why this might/might not be a bad approach. I don't see any cons outside of possibly being less explicit, but I think not worrying about simultaneous renders/refactoring to add new functionality to components trumps breaking variables down discretely myself and should probably be the rule vs exception so far.

I think on these cases that have multiple variables, you should try useReducer instead useState

@FredyC

This comment has been minimized.

FredyC commented Dec 4, 2018

I am wondering, isn't it a good time to lock this mega thread? Questions are mostly just repeating itself and no newcomer is probably going to read through everything in here. Smaller and focused threads might be a better idea, eg. Spectrum chat.

Nonetheless, I am unsubscribing, so have fun :)

@rob2d

This comment has been minimized.

rob2d commented Dec 4, 2018

@lasthrun useReducer can help in things with really intricate interactions or with defined actions/mutations, but in many use cases with state (e.g. combining 2 or 3 state values, simply deriving state/reacting to prop changes e.g. componentDidUpdate functionality), doesn't seem that it is what that is designed for.

@FredyC sorry for the impression; genuinely read through thread and hadn't encountered the question or a staightforward answer. But perhaps if questions actually are repeated, it may highlight some lacking parts of documentation (which is 100% understandable as this feature is still not in full prod version yet).

@timesp

This comment has been minimized.

timesp commented Dec 5, 2018

useReducer is prepared for the inside state of component , how I can get the state/dispatch from the outside component.
for example

const initialCountState = {count: 0};

function countReducer(state, action) {
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, {count: initialCount});
  return ();
}
const initialOtherState = {other: 0};

function otherReducer(state, action) {
}

function Other({initialState}) {
  const [state, dispatch] = useReducer(otherReducer, {other: initialState});
  // I want to get the state of Counter ,how ?
  return ();
}

How can I get the state/dispatch of Counter in Other?

@samsch

This comment has been minimized.

samsch commented Dec 5, 2018

@timesp Hooks are a way to manage state and side effects within a component. Between components, the interaction works just like it does with class components. It all breaks down to passing props (or passing through context) and lifting state up.

@Dudeonyx

This comment has been minimized.

Dudeonyx commented Dec 5, 2018

@timesp @samsch useContext?

@rob2d

This comment has been minimized.

rob2d commented Dec 5, 2018

@timesp

useReducer is prepared for the inside state of component , how I can get the state/dispatch from the outside component.
How can I get the state/dispatch of Counter in Other?

in this case, probably best approaches are:

  1. if you are not sure about the design/direction, use a global flux-based state system. This doesn't/probably should not be a replacement. Otherwise, @samsch suggestion to lift state makes sense.

  2. if it is for a standalone component (for example a file uploader), using a callback + handler seems like a decent approach. It depends on if your Component is designed to be in a standalone library or not.

Handling entire app state management via your local components though for anything non trivial/scaleable is likely going to cause a lot of headaches (both practically -- having a render-based component pasted invisibly onto a fixed part of your app at all times, and efficiency-wise -- with the lifecycle).

@ramdariro

This comment has been minimized.

ramdariro commented Dec 5, 2018

is it right todo solution?

function Todos() {
  const [todo, setTodo] = useState("");
  const [todos, setTodos] = useState([]);

  return (
    <div>
      <input
        type="text"
        onChange={({ target: { value } }) => setTodo(value)}
        value={todo}
      />
      <button
        onClick={() => {
          setTodo("");
          todo ? setTodos([...todos, { todo }]) : setTodos([...todos]);
        }}
      >
        Add Todo
      </button>
      <ul>{todos && todos.map((el, idx) => <li key={idx}>{el.todo}</li>)}</ul>
    </div>
  );
}

how to remove todo element from "todos"? 🤔

@dantman

This comment has been minimized.

dantman commented Dec 5, 2018

@ramdariro

  • You probably want to do setTodos(todos => [...todos, { todo }]) to update the value
  • Since you have add/delete operations it might be best to use useReducer instead of useState
  • You can remove elements using something like lodash's without
@timesp

This comment has been minimized.

timesp commented Dec 6, 2018

@samsch @Dudeonyx @rob2d Thanks for reply.

  • useContext may cause some some problems such as efficiency.
  • Lifting state up is an optional solution, but the problem is when I don't have a good design of the page, may cause recode the state of component.

Background:
Now I use the redux to manage the state of page, because I have the scene of several component to get state of each other.
So, When I see the [something, setSomething] pattern, I am thirsty for a replace solution of central state manage.

I want a distributed state management solution.

So I suppose as follow:

@share(state, dispatch)
function ShareComponent (props) {
      const [state, dispatch] = useReducer(0);
      // other code is here
}

when I in other component may be like this

function OtherCOmponent () {
     const [shareState, shareDispatch ] = ShareComponent.share;
     // so I can opt the state or dispatch of ShareComponent
}

May be the mobx is an opt ?

@timesp

This comment has been minimized.

timesp commented Dec 6, 2018

Happy to see the Lazy initialization.But I encountered a problem just like

const fetchData = async () => {
    const url = '//your url ';
    let res = await fetch(url).then(function (response) {
        return response.json();
    }).then(function (data) {
        return data;
    }).catch(function (e) {
        console.log("Oops, error");
    });
    return res;
}
export default function Dr() {
    const [name] = useState(async () => {
        const initName = await fetchData();
        return initName;
    });
    // I always get the 
    // Promise {<resolved>: {…}}
    console.log('name', name);
    return <p>{name}</p>
}

Why I can't get the real name? Or the lazy initialization is only just for synchronous function?

@FredyC

This comment has been minimized.

FredyC commented Dec 6, 2018

@timesp You get what you set :) Since you have async function as state initializer, you get the Promise :) You need to do useEffect for this.

@timesp

This comment has been minimized.

timesp commented Dec 6, 2018

@timesp You get what you set :) Since you async function as state initializer, you get the Promise :)

I think it should perform like

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

There is any diff between both?

@FredyC

This comment has been minimized.

FredyC commented Dec 6, 2018

@timesp If someExpensiveComputation is async function, you will still get the Promise :) Better handling of async operation will come with full Suspense next year, but until then you have to useEffect on a mount and setState when a result is obtained and also handle loading state.

@ElvenMonky

This comment has been minimized.

ElvenMonky commented Dec 6, 2018

What about second "callback" parameter to setState? Will the following also work? And if so where do we get updated state?

const [name, setName] = useState('Name');
...
setName(() => 'Other Name', () => console.log(name /*should be new value here*/));
@timesp

This comment has been minimized.

timesp commented Dec 6, 2018

@ElvenMonky ,the second function won't work.
if setName's parameter is a function , You should return a value

@ElvenMonky

This comment has been minimized.

ElvenMonky commented Dec 6, 2018

@timesp In my example, setName's first parameter is a function, that returns string. Actually a question was, whether code above will actually call a function provided as a second parameter.

Just to avoid any confusion, here is a code using classes (experimental syntax), that I would like to replicate:

class Example extends React.Component {
  state = { name: 'Name' };

  handleClick = () => {
    this.setState(() => ({ name: 'Other Name' }), () => console.log(this.state.name));
  };

  render() {
    ...
  }
}
@FredyC

This comment has been minimized.

FredyC commented Dec 6, 2018

@ElvenMonky There is no second argument to a setter that comes from useState. Don't confuse it with setState from class, that's something completely different.

@timesp

This comment has been minimized.

timesp commented Dec 6, 2018

@ElvenMonky FredyC is right.

@samsch

This comment has been minimized.

samsch commented Dec 6, 2018

@timesp

Background:
Now I use the redux to manage the state of page, because I have the scene of several component to get state of each other. So, When I see the [something, setSomething] pattern, I am thirsty for a replace solution of central state manage.

I want a distributed state management solution.

So I suppose as follow:

@share(state, dispatch)
function ShareComponent (props) {
      const [state, dispatch] = useReducer(0);
      // other code is here
}

when I in other component may be like this

function OtherCOmponent () {
     const [shareState, shareDispatch ] = ShareComponent.share;
     // so I can opt the state or dispatch of ShareComponent
}

Hooks don't provide any more of a replacement for centralized state than class components and the Context API do.

If you want to use the state and setter/dispatcher from a component (either from a Hooks-based component with useState/useReducer or from a class component with a custom dispatch method), the only way to do that is by passing the state and function (or a getter, store, observable, etc) down to children via props. The child can be a Context Provider, and then children rendered under that can use either the Consumer or useContext, but there must be that parent-child relationship.

In React trees, having a relationship where components share state in any other way is a bad pattern.

May be the mobx is an opt ?

MobX is often considered an alternative to Redux + react-redux as a state management solution, yes. I don't personally recommend MobX for reasons outside of the scope here, but you can evaluate the library yourself to see if it does what you need.

In general, a distributed state model is what you get when using class component state or Hooks useState/useReducer in more than one place. This is a fine pattern in React (though some would disagree), but you still need to follow the rules of downward flowing state and upward flowing events.

* useContext may cause some some problems such as efficiency.

Performance should not be your first consideration (except in rare circumstances), and you shouldn't worry about useContext efficiency until you actually have a problem. If it does become a problem, you can avoid rendering unnecessarily using tools such as PureComponent/shouldComponentUpdate and React.memo. You can also study how react-redux works in combination with memoized selectors (e.g., reselect) using a subscribable store as the value passed via context and only re-rendering the connected component if state that it actually uses changes.

* Lifting state up is an optional solution, but the problem is when I don't have a good design of the page, may cause recode the state of component.

Lifting state up often implies that your components will need to be changed if your initial plan or prototype doesn't match your needs. That's just part of the regular process of building an app :).

@andrewfluck

This comment has been minimized.

andrewfluck commented Dec 6, 2018

Another thing with hooks, and SFCs in general. If we were to make them powerful, we should a have a refs implementation. I have a menu component that accepts a child, and it gets the bounds of the child through refs. I had to wrap a component in a span in order for me to get the ref of the component.

@aditya81070

This comment has been minimized.

aditya81070 commented Dec 6, 2018

I am a student who just started working on react before last month. I liked the react already and always thought of this thing that why we can't do everything with functional component. Just watched the React Conf Keynote and I loved it how the react hooks works.
They are:-

  • Simpler
  • Reusable
  • Custom(Most Liked part)
    Surely will give a try to hooks in next project. Thanks React Team
@KpjComp

This comment has been minimized.

KpjComp commented Dec 11, 2018

after receiving community feedback.

Ok, here's my feedback. Go for it!!.

After playing with Hooks for the past few weeks, I've restructured a lot of code that was using classess, and now using Hooks, and it's way more maintainable and easy to follow. Some of these classes did some pretty complicated stuff with eventListeners / proxy's etc.

For me the greatest strength is separation of concerns. eg. All the code to perform some sort of action is all in the same place. The ability to have multiple hooks here is a real gem. This has allowed me to share way more code than was possible with classes.

One idea for the Dev's, is maybe have a trimmed down version of React that doesn't use classes. There might be some more optimisations & assumptions that could be performed,. eg. during render / diff stage no need to check for functional or instance of Component etc, and it would make the Lib even lighter than is. Of course this shouldn't be a high priority, but just something to maybe think about for the future.

So moving from proposal to actual, get's my vote.. 👍

@arthurgallo

This comment has been minimized.

arthurgallo commented Dec 11, 2018

Does anyone like a more explicit version of hooks? Maybe this has already been brought up elsewhere, sorry.

const Example = React.useHooks((props) => {
    return {
          count: useState(0),
          
    }
},
(props, hooks) => {
    return (<div>
        <p>Clicked {hooks.count[0]} times</p>
        <button onClick={() => hooks.count[1](hooks.count[0] + 1)}>Click me
        </button>
    </div>)
});
@kingdaro

This comment has been minimized.

kingdaro commented Dec 11, 2018

@arthurgallo Sebastian wrote up a response to alternate proposals (a lot which look similar to that one) #68 (comment)

@tommy-dev

This comment has been minimized.

tommy-dev commented Dec 12, 2018

I'm super excited about hooks as I never liked writing this.method = this.method.bind(this) 10000 times.
I started using it with aws amplify and gatsbyjs and found a potential issue with the connect component.
Maybe this is not react's problem but the library provider needs to fix. However, it is worth a mention the potential friction when using hooks with 3rd party react components.

import { Connect } from 'aws-amplify-react'
const MainPage = () => {

  return (
    <>
      <Connect query={graphqlOperation(listTodos)}>
        {({ data: { listItems } }) => (
          <>
            {/* this will throw hooks cannot be used in class error */}
            <HookComponentHere />
          </>
        )}
      </Connect>
    </>
  )
}

@Dudeonyx

This comment has been minimized.

Dudeonyx commented Dec 12, 2018

@tommy-dev I'm guessing that it's because is a class component and since you're using the as it's render child prop, the hooks are executed in the context of which is a class and will therefore throw an error.

I am probably wrong since if that was the case you shouldn't be the first person to get this error as this is a relatively common scenario

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment