Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I initialize a class instance in a stateless function component in React?

Using a stateful pattern, I usually initialize a kind of helper class in my constructor and consume its methods in some component lifecycle methods like below:

class StatefulComponent extends Component {
  constructor(props) {
    super(props);
    this.helper = new HelperClass();
  }

  componentDidMount() {
    this.helper.doSomething();
  }

}

Now, I wanted to convert the same logic into a stateless function component like this:

const StatelessFunction = (props) => {
   this.helper = new HelperClass();

   useEffect(() => {
     this.helper.doSomething();
   }, []);

}

But I worried when I saw that this component is being called every prop change from the beginning. And this made me think that my class instance is being created over and over. Am I wrong? Is there anything I can do for preventing re-creation of my class and use a ref instead?

I came across useRef but not sure if it fits my case.

like image 932
Nostromo Avatar asked Oct 08 '19 16:10

Nostromo


2 Answers

Looking back at this and some of the comments I can see that useMemo can not be trusted to not run the HelperClass constructor again and useRef will only set the helper after the first render because it's initial value can't be a function. Probably useState is the easiest and most reliable way to do this:

const [helper] = useState(()=>new HelperClass());

You can use useMemo to create an instance of HelperClass and useEffect to call it. Giving them both empty array of dependencies means they will only be called "on mount". I put on mount in quotes because memo will be called only on first render and effect will be called after first render cycle is finished.

const StatelessFunction = props => {
  const helper = useMemo(() => new HelperClass(), []);
  useEffect(() => {
    helper.doSomething();
  }, [helper]);
  return (<JSX />);
};

If the only thing you'll ever do is just call doSomething and never use the helper instance again you can just do it with useEffect:

useEffect(() => {
  new HelperClass().doSomething();
}, []);

If you do plan to use the helper instance at some later time then you could use the previous example with useMemo or useRef:

const helper = useRef();
useEffect(() => {
  helper.current = new HelperClass();
  //only called once after first render
  helper.current.doSomething();
}, []);
//you still have the helper instance as helper.current
like image 92
HMR Avatar answered Nov 12 '22 17:11

HMR


I'd go for useMemo solution, but here's how you can do it with useRef + useEffect to clear up some confusion:

const StatelessFunction = (props) => {
   const helper = useRef(); // ref is equivalent of instance prop

   useEffect(() => {
     helper.current = new HelperClass();
     helper.current.doSomething();
   }, []); // this is equivalent of componentDidMount
}
like image 28
marzelin Avatar answered Nov 12 '22 17:11

marzelin