Sventies
Sventies

Reputation: 2756

pass props with functional component without declaring function within component

I iterate over a list of items and want to call a function onClick(). Example below:

class App extends Component {
    state = { items: [1, 2, 3, 4] };

    click = i => console.log(i);

    render() {
        return (
            <div>
                {this.state.items.map(item => (
                    <div 
                        key={item} 
                        // this works fine, but I rather not declare functions within the render function / JSX
                        onClick={() => this.click(item)}
                    >
                        {`click on # ${item}`}
                    </div>
                ))}
            </div>
        )
    }
}

As you can see, I declare a function in the JSX. I do this over and over, but some time ago I learned that you should avoid declaring a function within the component JSX itself, for performance reasons.

Is this true, also for functional components? And if so, is there any other way to pass dynamic information (item, in this case) to a callback?

NOTE: I'm looking forward to using hooks at some point, but right now I just want to know a best practice without using them.

NOTE II: That being said, if you are sure that this is a relevant problem and it could not be solved until now because of hooks, I obviously would like to learn that :)

Upvotes: 0

Views: 1843

Answers (3)

guyg
guyg

Reputation: 95

You can create a parent component which will be responsible for looping through the items and passing the click handler down as a prop to each individual child component.

Then inside the child component you can easily reference the handler as this.props.onClick

Functions (objects) are passed by reference in JavaScript. By declaring it in the parent scope it will only take up space there. If initialized in the child, it will create space in memory for the function with each child component.

class Parent extends Component {
    state = { items: [1, 2, 3, 4] };

    parentClick(item) {
        // do something
        console.log(item);
    }
    render() {
        return (
            <div>
                {this.state.items.map(item => (
                  <Child item={item} onClick={this.parentClick}>
                ))}
            </div>
        )
    }
}



class Child extends Component {

    childClick(item) {
        this.props.onClick(item)
    }

    render() {
        return (
            <div>
                <p onClick={this.childClick(this.props.item)}>this.props.item</p>
            </div>
        )
    }
}

Upvotes: 0

Jonas Wilms
Jonas Wilms

Reputation: 138267

Is this true, also for functional components?

Well actually there is no functional component used in your code. And everything you do in a function that gets called very often (render() for example) causes performance to decrease, no matter wether that is a function or variable declaration or invokation. Wether that decrease matters is another thing.

And if so, is there any other way to pass dynamic information (item, in this case) to a callback?

You could .bind(...) it:

 onClick = {console.log.bind(console, item) }

but really, did you notice any delay on a rerender? Probably not, and if so that is not caused by the function declaration. Write code that looks beautiful to you, don't optimize for the compiler, let the compiler do that.

but some time ago I learned that you should avoid declaring a function within the component JSX itself

You shouldn't really avoid it, rather prefer other ways if possible. In this case there is not really a better way so go with it.

Upvotes: 1

Tnc Andrei
Tnc Andrei

Reputation: 1012

You can declare another method like this. Don't forget to use .bind. Otherwise, the method won't be called correctly.

class App extends Component {
    state = { items: [1, 2, 3, 4] };

    handleClick(item) {
        // do something
        console.log(item);
    }
    render() {
        return (
            <div>
                {this.state.items.map(item => (
                    <div 
                        key={item} 
                        // this works fine, but I rather not declare functions within the render function / JSX
                        onClick={this.handleClick.bind(this, item)}
                    >
                        {`click on # ${item}`}
                    </div>
                ))}
            </div>
        )
    }
}

Upvotes: 0

Related Questions