Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React functional component reinitialises local functions and variables on re render (React Hooks)

So I have started using React hooks now. I have been experimenting with the API for some time now. i really like the idea of bringing the state to functional components. but there is this one thing which keeps on bothering me and it doesn't feel right in the gut when i am trying to use it. I tried posting on RFCs but it's too crowded there now. everything seems lost there.

Here is a piece of code from my example.

import React, { useState } from "react";

function Counter() {
  const [counterState,incrementCounterState] =  useCommontState(0); 

  function doSomething (){
   // does something and then calls incrementCounterState
   // with the updated state.
  }

   return (
    <div>
      <p>{counterState}</p>
      <button onClick={incrementCounterState}>increase</button>
      ....
      .... // some jsx calling local scoped functions.
      ....
    </div>
    );
}

function useCommontState(defaultValue){
  var [state, setState] = useState(0);
  function increment(){
    setState(defaultValue+=1);
  }
  return [state, increment]
}

export default Counter;

I can easily take out state and setState methods out and create a custom hook but my problem is with the local functions that are used by the component. since state is now part of the component there will be cases where some logic will decide what to do next with the state.

Also, when the component re-renders on state change everything gets reinitialized. which is my problem. I know that useState has its own way of handling the issue. but my problem is with my own functions. the click handlers. on change events, callbacks for child components etc. all that will be reinitialized everytime the component renders. this doesn't feel right to me.

Are there any ways by which we can work around it. it's a new API. we are not even sure if it will make into react 17. but has anyone come across any better way to do it?

like image 693
hannad rehman Avatar asked Nov 06 '18 17:11

hannad rehman


3 Answers

You can always simplify the code to take functions out so that they aren't initialised always, by passing the required values as constants.

    import React, { useState } from "react";

    function doSomething (counterState, incrementCounterState){
       // does something and then calls incrementCounterState
       // with the updated state.
   }
    function Counter() {
      const [counterState,incrementCounterState] =  useCommontState(0); 

       return (
        <div>
          <p>{counterState}</p>
          <button onClick={incrementCounterState}>increase</button>
          ....
          .... // some jsx calling local scoped functions.
          ....
        </div>
        );
    }

    function increment(defaultValue, setState){
        setState(defaultValue + 1);
    }

    function useCommontState(defaultValue){
      var [state, setState] = useState(0);
      return [state, increment]
    }

    export default Counter;

Also in my opinion the function design being suggested in all the demos and docs is for people to get comfortable with it and then think about the re-initialization aspects. Also the cost that re-initialization would significanly be overpowered by the other benefits that it provides.

like image 197
Shubham Khatri Avatar answered Oct 09 '22 05:10

Shubham Khatri


I had the same concerns as well when I first saw the proposal, but this was addressed in the React Docs Hooks Proposal FAQ:

Are Hooks slow because of creating functions in render?

No. In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.

My takeaway is that although you have additional overhead now in the repeated declarations per render, you have additional wins elsewhere:

  • Hooks avoid a lot of the overhead that classes require, like the cost of creating class instances and binding event handlers in the constructor.

  • Idiomatic code using Hooks doesn’t need the deep component tree nesting that is prevalent in codebases that use higher-order components, render props, and context. With smaller component trees, React has less work to do.

Overall the benefits might be more than the downsides which makes hooks worth using.

like image 24
Yangshun Tay Avatar answered Oct 09 '22 06:10

Yangshun Tay


I'm using createOnce helper function to prevent reinitialises, But I'm not sure if it's correct or not.

utils/createOnce.js

import { useMemo } from 'react';

export const createOnce = toCreate => useMemo(() => toCreate, []);

SomeComponent.js

...

const someFunction = createOnce((counter) => {
  // whatever
  return counter + 1;
});

...
like image 21
Watcharaphat Manosatiankul Avatar answered Oct 09 '22 04:10

Watcharaphat Manosatiankul