How to avoid anonymous function in react - javascript

I have a scenario here
const Parent = () => {
handleClick = (id) => {
console.log(id)
}
return <div>
users.map((user, index) =>
<child key={index} onClick={(user.id)=>handleClick(user.id)} />)
</div>
}
The child is using React.memo so it won’t re-render unless its props are changed. I don’t want the child to be re-render when the parent renders but in this scenario it will re-render because I am using an anonymous function. I can put handleClick inside of useCallback but how can I avoid the anonymous function here. I have to use an anonymous function here because I am expecting some arguments here.

You can try using bind.
const Parent = () => {
handleClick = (id) => {
console.log(id)
}
return <div>
users.map((user, index) =>
<Child key={index} onClick={handleClick.bind(this, user.id)} />)
</div>
}
Here's the relevant section of the React docs: Passing Arguments to Event Handlers

Related

Cannot Access props inside function of functional component

I am trying to use props inside function of functional props.
const Inputs = (props) => {
console.log(props.printFirstArray);
const FirstInputSet = () => {
console.log(props.printFirstArray)
}
}
First console.log is logging the value of printFirstArray, but second console.log inside FirstInputSet() function is not logging anything.
Edit: Minimal Code
const Inputs = (props) => {
const FirstInputSet = () => {
return (
<>
<div className="first input-set">
{props.printFirstArray}
</div>
</>
);
}
const renderFirstInputSet = () => {
if (props.firstInputValue)
return <FirstInputSet />
else
return null;
}
return (
<>
{renderFirstInputSet()}
</>
);
}
Neither props.printFirstArray nor props.printSecondArray is not returning anything
From your code, FirstInputSet is a new component and you aren't passing any props.
return <FirstInputSet />
Try to pass your props to the child component but I recommend keeping the child component out of parent component.
OR
If you meant to use FirstInputSet as a function, you can modify the code like below
return FirstInputSet();

Questions about useCallback hook and anonymous function

When passing a callback function, especially when passing a parameterized function, I know that I should use the useCallback hook because the use of anonymous functions can adversely affect performance.
the example of anonymous function I said is like this.
import React, { useState } from 'react';
const Component = () => {
const [param, setParam] = useState('');
...
return (
...
<SomeComponent
onClick={() => setParam('parameter')}
{...others}
/>
);
}
In the process of converting an anonymous function to use this hook, I encountered an error saying 'Too many renders' or it didn't work properly.
But I don't know exactly in what situation and in what situation.
and I used useCallback like below.
import React, { useState, useCallback } from 'react';
const Component = () => {
const [param, setParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
);
}
However, when using an anonymous function to return within useCallback, it also worked.
This means code like here. (Only the differences compared to the code above.)
const handleClick = useCallback((params) => {
return () => setParam(params);
},[]);
In this situation, I wonder if it's worse than just using an anonymous function inside the useCallback if I simply use an anonymous function instead of using this hook.
const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
);
in the above code during first render, at this statement "onClick={handleClick('parameter')}" handleClick function is called with a string named "parameter". since handleClick has setParam("parameter"), it will update state. updating state will cause re-render which will again come to same statement "onClick={handleClick('parameter')}" causing infinte loop.
following code you added later works because you are not updating state, instead returning a function, which acts as onclick handler.
const handleClick = useCallback((params) => {
return () => setParam(params);
},[]);
better way of doing this shoud be as follwing,
import React, { useState, useCallback } from 'react';
const Component = () => {
const [param, setParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick}
{...others}
/>
);
}
coming back to your question , comparing performance depends on the other function definitions and render times of child compoents inside return function inside your Compoenent.
let's say you have another onclickHanldier named 'anotherHandleClick' inside your app.
then your component looks like this
const Component = () => {
const [param, setParam] = useState('');
const [anotherParam, setAnotherParam] = useState('');
const handleClick = (params) => {
setParam(params);
};
const anotherHandleClick =(params) => {
setAnotherParam(params);
};
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
<SomeComponent
onClick={antherHandleClick('parameter')}
{...others}
/>
);
}
in the above component when any one of "SomeCompoenent" clicked entiere "Component" re-renders, so the handler functions are newly defined.and when both 's does referential equality check on onclick handler functions, they believe it is new handler function which casues them to render both.
in that case it is better to use useCallBack hook as following,
const Component = () => {
const [param, setParam] = useState('');
const [anotherParam, setAnotherParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
const anotherHandleClick = useCallback((params) => {
setAnotherParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
<SomeComponent
onClick={antherHandleClick('parameter')}
{...others}
/>
);
}
in the above code when any one is clicked , state changes. then when rendering, useCallback make sure that onclick handler refernces didn't change. so having dependency of onclick handlers won't be rerendered.
so final thought is It's creating a function on each render in both cases. the second (because it'e wrapped in a useCallback) will return the memoized function created on the initial render
when to use useMemo or useCallback refer this
In your code:
const handleClick = useCallback((params) => {
setParam(params);
},[]);
You should pass the parameters into the second argment of useCallback like so:
const handleClick = useCallback((params) => {
setParam(params);
},[setParam,param, SETPARAMACTUALVALUE]);
// Change SETPARAMACTUALVALUE to whatever the variable name is for the `setParam` hook
useCallback hook usage will be better if you want to save the function until hook's dependency changed. It gives you better performance because the hook remember internal function.

Passing a value stored in a function within a child component to a parent in React

Below is my code.
CHILD COMPONENT
const onSave = () => {
var val = value
props.onSave();
}
<div onClick={() => { onSave() }}>
<CheckButton variant="contained" text-align='center' />
</div>
PARENT COMPONENT
export const Card = (props) => {
const onSave = (val) => { [I WANT TO ACCESS val within here] }
}
<TextInputContainer onSave={() => onSave()} />
Is there any way I can access that variable val inside the parent component? The code is truncated big-time. The actual code uses Redux.
The createRef of react helps u to access the value of a react component:
Reference (https://pt-br.reactjs.org/docs/hooks-reference.html)
constructor () {this.CheckButtonRef = createRef()}
onSave = () => {props.onSave(this.CheckButtonRef.current.value)}
<CheckButton ref = {this.CheckButtonRef}/>

React refs when parent and child are function components

I have two React components, Parent and Child. Both must be function components. I am trying to change the state of Child from Parent. I believe the best way to do this is using refs, but I haven't been able to get it to work.
I've tried creating a ref in Parent and passing it down to child, but this causes an error. I considered forwardRef() but I'm not sure that will work either.
const Parent = () => {
const ref = React.useRef();
const closeChild = () => {
ref.current.setOpen(false);
};
return (
<div>
<Child ref={ref} onClick={closeChild} />
</div>
);
};
const Child = () => {
const [open, setOpen] = useState(false);
return (
<div>
{open ? <p>open</p> : <p>closed</p>}
</div>
);
};
The code as it is now produces this error message:
react-dom.development.js:506 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?`
Only stateful React components can expose the ref automatically. If using functional component, I think you'll need to use forwardRef for the child component:
e.g.
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
From the docs,
Refs provide a way to access DOM nodes or React elements created in the render method.
refs are not meant for changing the state.
You actually need useState in parent component, and later you can manage it from child component.
const Parent = () => {
const [open, setOpen] = useState(true);
const toggleChild = () => {
setOpen(!open)
};
return (
<div>
<Child onClick={toggleChild} open={open}/>
</div>
);
};
const Child = (props) => {
return (
<div>
{props.open ? <p onClick={props.onClick}>open</p> : <p onClick={props.onClick}>closed</p>}
</div>
);
}
Demo
I don't think you need refs here, you should really try to avoid them.
avoid using refs for anything that can be done declaratively. They are useful for:
1.Managing focus, text selection, or media playback.
2.Triggering imperative animations.
3.Integrating with third-party DOM libraries.
https://reactjs.org/docs/refs-and-the-dom.html
why not just send the value into the child through props?
const Parent = () => {
const [open] = useState(false);
const toggleChild = () => {
this.setState(prevState => {open: !prevState.open});
};
return (
<div>
<Child onClick={toggleChild} open={this.state.open}/>
</div>
);
};
const Child = (props) => {
return (
<div>
{props.open ? <p>open</p> : <p>closed</p>}
</div>
);
};
EDIT: forgot you were using hooks. This is the way then
const [open, setOpen] = useState(false);
const toggleChild = () => {
setOpen(!open);
};
return (
<div>
<Child onClick={toggleChild} open={open}/>
</div>
);
EDIT 2: #ravibagul91 pointed out that you need to put the onClicks in the children <p> elements as well, look at his answer

React event listeners on array of elements

My React render function eventually renders a set of elements:
data.map((element) => {
return <Object onChange={this.onObjectChange} />;
});
My question is what is the appropriate way to figure out which object had its onChange method called when I receive the callback?
The first parameter to the onSubjectChange function will have your event which contains the event information.
Hope that helps!
If you can, pass the ID or element to the component you're creating, then pass that reference back to your event handler.
handleObjectChange = id => {
const object = data.find(id)
}
render() {
return data.map((element, index) => (
<Object onChange={this.handleObjectChange} key={element.id} id={element.id} />
))
// or just pass element={element} to track the object itself, why not?
// after all, every array item's key must be unique
}
In Object...
function change() {
const { onChange, id } = this.props
onChange(id)
}
Is your Object closed, or prefer not to add an extra property? You could try wrapping it.
class IdentifiedButton extends Component {
handleClick = (event, ...args) => {
const { id, onClick } = this.props
onClick(event, id, ...args)
}
render() {
const { id, onClick, ...props } = this.props
return <Button onClick={this.handleClick} {...props} />
}
}
Wrap the callback in a function and pass an identifier:
data.map(element => <Object onChange={event => this.onObjectChange(element.id, event)} />);
If rerendering your Object component won't cost you that much I would go with something like this
data.map(element => <Object onChange={() => this.onObjectChange(element)} />);
If Object component is quite heavyweight you better pass element to Object component and then pass it to onChange callback
data.map(element => (
<Object
key={element.id}
onChange={this.onObjectChange}
element={element}
/>
);
class Object extends React.Component {
handleChange = () => this.props.onChange(this.props.element)
render(){
return (
<input type='text' onChange={this.handleChange} />
)
}
}
To avoid creating an anonymous function in every render, you could have a function that creates the handlers like this (I am using Todo App as an example):
createOnChangeEventHandler(todo) {
const { onToggleClick } = this.props;
this.handlers = this.handlers || {};
if (!this.handlers[todo.id]) {
this.handlers[todo.id] = () => onToggleClick(todo.id);
}
return this.handlers[todo.id];
}
...
Then in render()
{todos.map(todo => (<Todo key={todo.id} onClick={this.createOnChangeEventHandler(todo)} />))}

Categories

Resources