193

I'm trying to convert this cool <canvas> animation I found here into a React reusable component. It looks like this component would require one parent component for the canvas, and many children components for the function Ball().

It would probably be better for performance reasons to make the Balls into stateless components as there will be many of them. I'm not as familiar with making stateless components and wondered where I should define the this.update() and this.draw functions defined in function Ball().

Do functions for stateless components go inside the component or outside? In other words, which of the following is better?

1:

const Ball = (props) => {
    const update = () => {
        ...
    }

    const draw = () => {
        ...
    }

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

const Ball = (props) => {
    return (
       ...
    );
}

What are the pros and cons of each and is one of them better for specific use cases such as mine?

2
  • Can you post the existing code so we see how it will get used? Commented Sep 10, 2017 at 7:31
  • @Scimonster I posted it in an embedded link, maybe you missed it. Here's the link: codepen.io/awendland/pen/XJExGv Commented Sep 10, 2017 at 7:32

5 Answers 5

230

The first thing to note is that stateless functional components cannot have methods: You shouldn't count on calling update or draw on a rendered Ball if it is a stateless functional component.

In most cases you should declare the functions outside the component function so you declare them only once and always reuse the same reference. When you declare the function inside, every time the component is rendered the function will be defined again.

There are cases in which you will need to define a function inside the component to, for example, assign it as an event handler that behaves differently based on the properties of the component. But still you could define the function outside Ball and bind it with the properties, making the code much cleaner and making the update or draw functions reusable:

// you can use update somewhere else
const update = (propX, a, b) => { ... };
    
const Ball = props => (
  <Something onClick={update.bind(null, props.x)} />
);

If you're using hooks, you can use useCallback to ensure the function is only redefined when any of its dependencies change (props.x in this case):

const Ball = props => {
  const onClick = useCallback((a, b) => {
    // do something with a, b and props.x
  }, [props.x]);

  return (
    <Something onClick={onClick} />
  );
}

This is the wrong way:

const Ball = props => {
  function update(a, b) {
    // props.x is visible here
  }
    
  return (
    <Something onClick={update} />
  );
}

When using useCallback, defining the update function in the useCallback hook itself or outside the component becomes a design decision more than anything: You should take into account if you're going to reuse update and/or if you need to access the scope of the component's closure to, for example, read/write to the state. Personally I choose to define it inside the component by default and make it reusable only if the need arises, to prevent over-engineering from the start. On top of that, reusing application logic is better done with more specific hooks, leaving components for presentational purposes. Defining the function outside the component while using hooks really depends on the grade of decoupling from React you want for your application logic.

Another common discussion about useCallback is whether to always use it for every function or not. That is, treat is as opt-in or always recommendable. I would argue to always use useCallback: I've seen many bugs caused by not wrapping a function in useCallback and not a single scenario where doing so affects the performance or logic in any way. In most cases, you want to keep a reference while the dependencies don't change, so you can use the function itself as a dependency for other effects, memos or callback. In many cases the callback will be passed as a prop to other elements, and if you memoized it with useCallback you won't change the props (thus re-render) other components independently of how cheap or costly that would be. I've seen many thousands of functions declared in components and not a single case in which using useCallback would have any down side. On the other hand most functions not memoized with useCallback would eventually be changed to do so, causing serious bugs or performance issues if the developer doesn't recognize the implications of not doing so. Technically there is a performance hit by using useCallback, as you would be creating and additional function but it is negligible compared to the re-declaration of the function that always has to happen either you use useCallback or not and the overall footprint of React and JavaScript. So, if you are really concerned about the performance impact of useCallback versus not using it, you should be questioning yourself if React is the right tool for the job.

Sign up to request clarification or add additional context in comments.

28 Comments

Thanks Marco, that clears things up a bit. The thing I'm confused about in my case is related to the this.draw function inside the Ball. It uses the ctx from what would be the parent's <canvas> and also uses the this keyword for what would be the child Ball component. What would be the best way to integrate implement the stateless component so both of those properties are accessible?
there is no this when using stateless functional components, have that in mind. For the canvas context, you would have to pass it to every single Ball, that doesn't sound good at all.
@MarcoScabbiolo no no, that's not my case, already using arrow functions natively for quite a long time, since the only browser not supporting them is IE. Actually I managed to find this comment from one article where it's actually claimed that bind specifically in Chrome earlier than 59 was even slower than arrow functions. And in Firefox it's as well quite a while since they both perform with the same speed. So I'd say in such cases there is no difference what is the preferred way :)
@MauricioAvendaño either way works, but it is a bad practice for the Something component to know that there is a prop X in its parent component as it makes it aware of its context. The same happens for the question you're asking and the sample code I've written, it depends on the context, which is ignored for the sake of simplicity.
@Atif Depends on the component and its children. Understand why: reactjs.org/docs/reconciliation.html Measure it: reactjs.org/docs/optimizing-performance.html
|
20

You can place functions inside stateless functional components:

function Action() {
    function handlePick(){
        alert("test");
    }

    return (
        <div>
            <input type="button" onClick={handlePick} value="What you want to do ?" />
        </div>
    )
}

But it's not a good practice as the function handlePick() will be defined every time the component is rendered.

It would be better to define the function outside the component:

function handlePick(){
    alert("test");
}

function Action() {
    return (
        <div>
            <input type="button" onClick={handlePick} value="What you want to do ?" />
        </div>
    )
}

2 Comments

If it's not good practice, how do you write functions inside function components that would've been class methods in a class-style React component? These methods typically need access to state and so must be inside the function.
@Jez use the useCallback hook (my preferred way) or pass it everything it needs
17

If you want to use props or state of a component in a function, that should be defined in the component with useCallback.

function Component(props){
  const onClick=useCallback(()=>{
     // Do some things with props or state
  },[])

  return <Something {...{onClick}} />
}

On the other hand, if you don't want to use props or state in a function, define that outside of the component.

const computeSomethings=()=>{
   // Do some things with params or side effects
}

function Component(props){
  return <Something onClick={computeSomethings} />
}

For HTML tags you don't need useCallback because that will handle in the react side and will not be assigned to HTML.

function Component(props){
  const onClick=()=>{
     // Do some things with props or state
  }

  return <div {...{onClick}} />
}

Edit: Functions in hooks

For using function in hooks, for example useEffect, my suggestion is defining a function inside useEffect. If you're worried about DRY, make your function pure call it in hook and give your params to it. What about hooks deps? You should/could add all of your params to hooks deps, but useEffect just needs deps which should affect for them changes.

1 Comment

can you give an example of using hook for method inside functional component? (not set state method)
5

We can use the React hook useCallback as below in a functional component:

const home = (props) => {
    const { small, img } = props
    const [currentInd, setCurrentInd] = useState(0);
    const imgArrayLength = img.length - 1;
    useEffect(() => {
        let id = setInterval(() => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {
                setCurrentInd(0)
            }
        }, 5000);
        return () => clearInterval(id);
    }, [currentInd]);
    const onLeftClickHandler = useCallback(
        () => {
            if (currentInd === 0) {

            }
            else {
                setCurrentInd(currentInd => currentInd - 1)
            }
        },
        [currentInd],
    );

    const onRightClickHandler = useCallback(
        () => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {

            }
        },
        [currentInd],
    );
    return (
        <Wrapper img={img[currentInd]}>
            <LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}>
                <img src={Icon_dir + "chevron_left_light.png"}></img>
            </LeftSliderArrow>
            <RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}>
                <img src={Icon_dir + "chevron_right_light.png"}></img>
            </RightSliderArrow>
        </Wrapper>);
}

export default home;

I'm getting 'img' from it's parent and that is an array.

2 Comments

Thank you - I had a feeling there was a hook so that we could list dependencies of a const function inside of a functional compoenet!
UseCallback is only useful for memorizing the function, so that on each render the function can be memorized itself, which takes dependencies of a props or state.
0
import React, { useState } from 'react';
function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);
  const a = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={a}>Click me</button>
    </div>
  );
}
export default Example;

1 Comment

I would recommend not answering a question that has been around for 3 years and has an answered question with so many up votes. You probably wont get yours voted on.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.