How to get multiple refs to use the same handleClick function? - reactjs

I have a few similar elements:
<div ref={this.ref1} onClick={this.handleClick} className="icon-wrapper"></div>
<div ref={this.ref2} onClick={this.handleClick} className="icon-wrapper"></div>
<div ref={this.ref3} onClick={this.handleClick} className="icon-wrapper"></div>
In my constructor, I declare these refs:
constructor(props) {
super(props);
this.ref1 = React.createRef();
this.ref2 = React.createRef();
this.ref3 = React.createRef();
}
I'm trying to pass these refs to their respective handleClick() functions. Something like:
<div ref={this.ref1} onClick={this.handleClick(ref1)} className="icon-wrapper"></div>
or
<div ref={this.ref1} onClick={this.handleClick(this.ref)} className="icon-wrapper"></div>
And I'm trying to handle the ref in the handleClick function like this:
handleClick = (ref) => {
console.log(ref);
}
And just as a ridiculously wild guess, I tried:
handleClick = () => {
console.log(this.ref.current);
}
That all seems dead wrong because onClick={this.handleClick(ref)} would invoke the function immediately, which is not what is wanted from a click function.
How do you pass different refs to the same function so that the function knows who called it?

The main issue here is you are invoking the function immediately. Thats what applying () does to the end of a function. Instead you need to pass a callback function that needs to be invoked by the event. This is done by passing the function reference.
You can use bind here if you need to pass a value. .bind will return a new function to be invoked. null as the first argument is for the this context, null wont change its context assuming its already been bound to your class. If you need to bind this method you can just pass this through instead of null.
I would recommend passing something to map though instead of the whole ref, like so
<div ref={this.ref1} onClick={this.handleClick.bind(null, 'ref1')} className="icon-wrapper"></div>
then you would access like so
handleClick = (ref, event) => {
if (!this[ref]) return
console.log(this[ref].current);
}
Most use cases for passing a parameter to your function would be because you need a specific key. In this case it looks like you just want to access the element itself. You can get that from the event itself.
<div onClick={this.handleClick} className="icon-wrapper"></div>
handleClick = (event) => {
console.log(event.target);
}

The answer to your question is to use a lambda method,
<div ref={this.ref1} onClick={() => this.handleClick(this.ref1)} className="icon-wrapper"></div>
<div ref={this.ref2} onClick={() => this.handleClick(this.ref2)} className="icon-wrapper"></div>
<div ref={this.ref3} onClick={() => this.handleClick(this.ref3)} className="icon-wrapper"></div>
But if what you want to do is to access the target that fired the event, you can use
handleClick = (event) => {
console.log(event.target);
}
<div onClick={this.handleClick} className="icon-wrapper"></div>
Edit: Here are some alternatives with better (marginal) performance.
handleClick1 = () => this.handleClick(this.ref1)
handleClick1 = this.handleClick.bind(this, this.ref1)
render() {
return (<div ref={this.ref1} onClick={this.handleClick1} className="icon-wrapper"></div>)
}

Related

Why does the onClick hook get called immediately when the useMutation hook is called? [duplicate]

I pass 2 values to a child component:
List of objects to display
delete function.
I use a .map() function to display my list of objects(like in the example given in react tutorial page), but the button in that component fires the onClick function, on render(it should not fire on render time). My code looks like this:
module.exports = React.createClass({
render: function(){
var taskNodes = this.props.todoTasks.map(function(todo){
return (
<div>
{todo.task}
<button type="submit" onClick={this.props.removeTaskFunction(todo)}>Submit</button>
</div>
);
}, this);
return (
<div className="todo-task-list">
{taskNodes}
</div>
);
}
});
My question is: why does onClick function fire on render and how to make it not to?
Because you are calling that function instead of passing the function to onClick, change that line to this:
<button type="submit" onClick={() => { this.props.removeTaskFunction(todo) }}>Submit</button>
=> called Arrow Function, which was introduced in ES6, and will be supported on React 0.13.3 or upper.
Instead of calling the function, bind the value to the function:
this.props.removeTaskFunction.bind(this, todo)
MDN ref: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
The Problem lies in how you pass your function
At the moment you are not passing the function but Calling it instead:
<Button onClick={yourFunction()} />
You can Fix this in two ways:
<Button onClick={() => yourFunction(params)} />
Or if you dont have any params:
<Button onClick={yourFunction} />
The value for your onClick attribute should be a function, not a function call.
<button type="submit" onClick={function(){removeTaskFunction(todo)}}>Submit</button>
you need to use an arrow function with onClick in order to prevent immediately invoke.
so if your button looks like this :
<button onClick={yourfunctionname()} />
it must be like this :
<button onClick={() => yourfunctionname(params)} />
JSX is used with ReactJS as it is very similar to HTML and it gives programmers feel of using HTML whereas it ultimately transpiles to a javascript file.
Writing a for-loop and specifying function as
{this.props.removeTaskFunction(todo)} will execute the functions
whenever the loop is triggered .
To stop this behaviour we need to return the function to onClick.
The fat arrow function has a hidden return statement along with the bind
property. Thus it returns the function to OnClick as Javascript can
return functions too !!!!!
Use -
onClick={() => { this.props.removeTaskFunction(todo) }}
which means-
var onClick = function() {
return this.props.removeTaskFunction(todo);
}.bind(this);
For those not using arrow functions but something simpler ... I encountered this when adding parentheses after my signOut function ...
replace this <a onClick={props.signOut()}>Log Out</a>
with this <a onClick={props.signOut}>Log Out</a> ... ! 😆
JSX will evaluate JavaScript expressions in curly braces
In this case, this.props.removeTaskFunction(todo) is invoked and the return value is assigned to onClick
What you have to provide for onClick is a function. To do this, you can wrap the value in an anonymous function.
export const samepleComponent = ({todoTasks, removeTaskFunction}) => {
const taskNodes = todoTasks.map(todo => (
<div>
{todo.task}
<button type="submit" onClick={() => removeTaskFunction(todo)}>Submit</button>
</div>
);
return (
<div className="todo-task-list">
{taskNodes}
</div>
);
}
});
I had similar issue, my code was:
function RadioInput(props) {
return (
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="inlineRadioOptions" id={props.id} onClick={props.onClick} value={props.label}></input>
<label className="form-check-label" htmlFor={props.id}>{props.label}</label>
</div>
);
}
class ScheduleType extends React.Component
{
renderRadioInput(id,label)
{
id = "inlineRadio"+id;
return(
<RadioInput
id = {id}
label = {label}
onClick = {this.props.onClick}
/>
);
}
Where it should be
onClick = {() => this.props.onClick()}
in RenderRadioInput
It fixed the issue for me.
It is possible to achieve this even in more readable way than:
<button onClick={() => somethingHere(param)}/>
const Comp = () => {
const [triggered, setTriggered] = useState(false);
const handleClick = (valueToSet) => () => {
setTriggered(valueToSet);
};
return (
<div>
<button onClick={handleClick(true)}>Trigger</button>
<div>{String(triggered)}</div>
</div>
);
};
That way it won't fire the state setter and won't cause too many re-renders compared to <button onClick={setTriggered(true)}/>
which is okay if you don't have any params to pass to the function.
That's because you are calling the function directly instead of passing the function to onClick
If you have passed down onClick={onClickHandler()} then, the function onClickHandler() will be executed during the time of rendering too, the () instructs to execute the function as soon as it is rendered , which is not desired here , instead we use onClick={onClickHandler} , this will execute the onClickHandler only when the specified event occurs. But if we want to pass down a argument along with the function then we can make use of ES6 arrow function.
For your Case :
<button type="submit" onClick={() => this.props.removeTaskFunction(todo)}>Submit</button>
Bit late here but here is the simple answer.
direct approach will trigger by itself due to JS DOM rendering
onClick={this.props.removeTaskFunction(todo)}
anonymous arrow function approach. it will trigger on click
onClick={()=>this.props.removeTaskFunction(todo)}
You are not passing the function as an argument you are calling it directly that why it launches on the render.
HOW TO FIX IT
there are two ways:
First
<Button onClick={() => {
this.props.removeTaskFunction(todo);
}
}>click</Button>
OR
Just bind it
this.props.removeTaskFunction.bind(this,todo);

Initiate function/event from child to parent

In my parent component I have a function/event that looks like this:
const onClick = e => {
e.preventDefault();
POST('/api', { data: data }).then(
async (response) => {
const json = await response.json()
setData(json.data)
}
)
}
On the parent component, this is initiated by the following:
<button type="button" onClick={onClick}>Click me</button>
However, I also have a child component, where I would like a click event in that to also initiate this function/event and a . How is that done ?
I tried just doing something like:
<ChildComponent onclick={onClick} data={data} setData={setData} />
And then in the child component just doing something like:
<div onClick={() => {props.setData(i); props.onClick;}}>
But that doesn't seem to work.
So any hints to what I'm doing wrong here ?
I am not sure if this will fix your problem, but it seems you are using incorrectly the function prop.
Try this:
<div onClick={(e) => {props.setData(i); props.onClick(e);}}>
You can use functions in onClick in 2 ways:
1 - Assign the prop function itself to the onClick:
<div onClick={props.onClick}>
2 - Assign a callback to the onClick which calls the function with the event:
<div onClick={(e) => props.onClick(e)}>
To summarize: the onClick needs to receive a function, but your problem was you were assigning a callback (this is correct) which was not calling the props.onClick one
[you were doing props.onClick instead of props.onClick()]
You should update the state in the parent, not in the child. I am not sure why you are calling props.setData in the child when you can handle that in the parent onClick function.

How do i setState onClick

Hi so i got my react function with a state that i use to know which componont should be rendered at a given time; here is my main App Function (i only include the function not the rest of class since it is not relevent)
My app function
function App(){
const [menuState, setMenuState] = useState('journal');
return(
<>
<button onClick={setMenuState('journal')}>JOUNRAL</button>
<button onClick={setMenuState('stats')}>STATS</button>
<MenuHandler menu={menuState}/>
</>
);
}
My MenuHandler function
function MenuHandler(props) {
const menu = props.menu;
if(menu==="journal")
return (<Journal />);
if(menu==="stats")
return (<Stats />);
return (<Journal />);
}
When i do this i get an infinite loop and i can't figure out why.
The problem is that you are calling setMenuState on each render, therefore the infinite loop. You are not assigning it to the onClick handler, pass an arrow function like this to solve it:
<button onClick={() => setMenuState('journal')}>JOUNRAL</button>
<button onClick={() => setMenuState('stats')}>STATS</button>

How to pass parameters to a function when using React stateless components?

I have a component like this (very simplified version)
const MyComponent = () => {
const handleClick = param => {
console.log(param);
}
return (
<Link to={'/'} onClick={handleClick} value={'a string'}>
);
}
How can I pass a parameter to the handleClick function?
you have to pass a function to onClick. onClick will always call your functions with an event parameter. So if you pass a function like this:
onClick={handleClick}
It will be called like this:
onClick={(event) => handleClick(event)}
So to pass parameters to a function you can do something like this instead:
onClick={() => handleClick(parameters)}

Reactjs not updating dom classes after splicing state

Hello I am new to react and have a question with changing classes and animation onClick.
I am trying to move up and down only with css classes that I add or remove to an array that is in my className.
app.js i have this in state
updown: ["containerMG"],
and here is how i render my components in app.js
render() {
return (
<div>
<div className="movies-container">
{this.state.filmovi.map((film, index) => {
return <Film
naslov={film.naslov}
naslovnaSlika={film.naslovnaSlika}
key={film.id}
openFilm={() => this.injectFilm(index)}/>
})}
</div>
<Gallery />
<ContainerMG
selectedFilm={this.state.selectedFilm}
klasa={this.state.updown}
zatvori={this.closePreview}/>
</div>
);
}
my component looks like this
const ContainerMG = (props) => {
return (
<div className={props.klasa.join(' ')}>
<img onClick={props.zatvori} src="xxx" alt="close" className="close-popup" />
<p>{props.selectedFilm.naslovFilma}</p>
</div>
)
}
this is how the div moves up
injectFilm = (filmIndex) => {
this.state.updown.push("position-top");
const selectedFilm = this.state.filmovi.find((film, index) => index === filmIndex)
this.setState((prevState) => ({
selectedFilm
}))
}
this is how i tried to move it down
closePreview = () => {
this.state.updown.splice(1);
}
i am pretty sure i should not change the state directly, and also when i change it to remove the "position-top" class the dom doesn't reload.
thank you for all the help in advance and if i didn't show something that you need please do write.
You're right, you should never change the state directly like that, but rather, use a setState() method. Doing this.state.updown.push("position-top"); will mutate it. Let's say you want to remove whatever is last from the array, you could do something like:
const {updown} = this.state;
updown.pop();
this.setState({updown: updown});
Which would cause a re-render. Treat the state as if it were immutable.

Resources