I am dynamically rendering react components as:
{map.call(this.state.text, (c, i) => (<Char
click={() => this.deleteCharHandler(i)}
charVal={c}
key={i}/>))}
Where there render method of the Char component is:
render() {
return(
<div className="char" onClick={this.props.click}>
{this.props.charVal}
</div>
)
That works and deleteCharHandler is appropriately called with the correct value of i.
However, I would like to avoid having to pass the handler function as a prop. After all, onClick of the div is simply executing the click property, which is an anonymous function calling deleteCharHandler. I tried to refactor as:
{map.call(this.state.text, (c, i) => (<Char
onClick={() => this.deleteCharHandler(i)}
charVal={c}
key={i}/>))}
Thus adding a react event listener to the Char element. And removed the onClick listener from the div in the Char component.
...And it doesn't work.
Why? Am I not just moving the onClick listener one level up?
You must note that passing any function to a React component is just passed down as a prop and not treated as a event listener. In order for the onClick to work it needs to be passed on to the DOM
Now either you can explicitly pass it on to div within Char component like do
render() {
return(
<div className="char" onClick={this.props.click}>
{this.props.charVal}
</div>
)
or you can destructure the other values and pass on the rest values using rest spread syntax. Either way you need to pass the handler to the DOM element
render() {
const {charVal, ...rest} = this.props;
return(
<div className="char" {...rest}>
{charVal}
</div>
)
The above method gives you the flexibility to pass on props required to the div element without having to mention them explicitly. However you must be careful that you aren't passing non required values to the div element
Related
In React, I can call the function in onClick in two different ways.
First Method: Arrow Function
const Modal = ({isOpen, toggle, children}) => {
<div onClick={() => toggle()}>
<div>
{children}
</div>
</div>
}
Second Method: Without brackets
const Modal = ({isOpen, toggle, children}) =>
{
return(
<div onClick={toggle}>
</div>
)
}
What is the difference between them? () => toggle() <-> toggle
The first way, onClick={() => toggle()}, creates a new function and assigns it to the onClick property. That function, when called, will call toggle with no arguments. A new function is created every time that component is rendered.
The second way, onClick={toggle}, does not create a new function, it directly assigns toggle to the onClick property. That means that when it's called, it receives any arguments that are passed (even if it doesn't expect any).
Each can be appropriate, depending on what you want to do. If toggle expects the arguments the click event will pass it, you're better off with the second way since you aren't creating a new function every time. If it doesn't, in general it's best not to set it up to receive an argument it doesn't expect.
The fact that the first way creates a new function on every render probably doesn't matter when you do this with a DOM element like a div, but suppose you're passing a function to a complex component that takes time to render and that optimizes render (avoiding re-rendering if its props don't change, via React.memo, PureComponent, shouldComponentUpdate, etc.):
return <SomeComplexComponent onSomething={() => toggle()} />
In that case, you might be best off "memoizing" the function (in this case, via useCallback or useMemo, usually) so you don't pass a new one to the component every time, so the complex component doesn't think it needs to re-render every time.
const onSomething = useCallback(() => toggle(), [toggle]);
// ...
return <SomeComplexComponent onSomething={onSomething} />
Although that still creates a new function on every render (so it can be passed into useCallback), useCallback will return the previous version of the function if toggle hasn't changed, which allows SomeComplexComponent to avoid re-rendering.
But there's no need for that when passing this to an HTML element or a simple component.
1st kind of code
onClick={toggle}
In this kind of onClick method you can call only one function inside onClick.
2nd kind of code
Suppose you need to call multiple functions inside the onClick. Then you have to use
onClick={() => toggle()}
Exmaple:-
OnClick = {()=>{
toggle1();
toggle2();
}}
And also you can pass parameters inside the function. using 2nd type like below.
Example: -
OnClick = {()=>{
person(name);
}}
In () => toggle() you are adding an anonymous function and then calling toggle function. If you want to pass in some arguments to toggle function, you can use this method. eg () => toggle(id, name..etc)
The other method simply add event handler and that function will receive event as argument.
I'm not sure exactly how to word this, so I'll do some pseudo code to illustrate:
I have a main component that has a map function that is iterating over an array of values. Inside the map function is a functional prop that references a handler function. I want to be able to not only return a value from the onClick, but I need to access variables that are still inside the mapped instance of the component.
parent
[1,2,3].map(num => ( <child funcProp={handleClick} )
in child:
const {funcProp} = props
---code that does calc or state
funcProp(res)
I'm getting the result I want from the child component, but I need a parameter from the invoking map as well. So for instance, I'd want (if we're at number 2 in the map) I want to return the res from the child component with the num 2 for the handler function. I'm sure there's some syntax that I am forgetting.
The rules of closures still apply in React. Create an anonymous function that calls handleClick itself. This function will have access to the map callback arguments:
[1,2,3].map((num) => {
return <child funcProp={() => handleClick(num)} />
})
Note that with this pattern, handleClick will no longer receive arguments from a call to funcProp in the child component. If you still want handleClick to receive these arguments, you must pass them along explicitly:
[1,2,3].map((num) => {
return <child funcProp={(...args) => handleClick(num, ...args)} />
})
Or, if you want num to be the last argument to handleClick:
[1,2,3].map((num) => {
return <child funcProp={(...args) => handleClick(...args.concat(num))} />
})
You could avoid the anonymous function though, and make use of Function.prototype.bind to create a bound copy of handleClick with a fixed first argument of num:
[1,2,3].map((num) => {
return <child funcProp={handleClick.bind(null, num)} />
})
In this case, we bind handleClick to the context null. This may or may not be appropriate for your use case - something to consider.
You can create a function which will let you access the value you want
[1,2,3].map(num => ( <child funcProp={() => handleClick(num)} )
Or pass the value in the child props as well
[1,2,3].map(num => ( <child num={num} funcProp={handleClick} )
In this case you might need to binding with "this" while passing the function.
If i use the example you mentioned then maybe this could help -
handleClick.bind(this)
The answer is probably, yes. Because it works, and we use it all day, I know. I am asking to be %100 sure and for future learners of React. I couldn't see it in the official docs of React, except giving an example for passing multiple arguments to event handlers alongside the event object. So for example: As you can see onFormSubmit, although not having an event argument inside the JSX reference, it has access to the event object to do stuff (preventing page refresh on this example) at the execution.
If you write the onFormSubmit as an inline arrow function like onChange handler, you need to pass the event object, then it is not automatic.
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = { term:''}
this.onFormSubmit = this.onFormSubmit.bind(this)
}
onFormSubmit(event) {
event.preventDefault()
console.log(this.state.term)
}
render() {
return (
<div className="ui segment" >
<form onSubmit={this.onFormSubmit} className="ui form" >
<div className="field" >
<label>Search</label>
<input type="text"
value = {this.state.term}
onChange={event => this.setState({ term: event.target.value })} />
</div>
</form>
</div>
)
}
}
export default SearchBar
You have defined the function to your onChange event handlers which calls the submit method passing the necessary arguments implicity.
There is nothing special about event handlers in React. Every function, if defined works this way.
const a = [1, 2, 3];
function print(elem, idx) {
console.log(elem, idx);
}
// here you have defined the print function.
// the elem and idx are passed implicitly to run the function.
a.forEach(print);
or
// now, rather than defining the function here.
// you have used another arrow function.
// then you need to pass the arguments explicitly.
a.forEach((elem, idx) => print(elem, idx));
React approaches the event handling a little bit differently, using Synthetic Events but this is how callback handlers work generally. If you use a function reference there, the event object is the only argument passed to your function. So, if the event object is the only argument you want to get then you don't need to use an arrow function.
If you want to pass other variables alongside with the event object, then you can use an arrow function.
<form onSubmit={e => this.onFormSubmit(e, otherVar)} className="ui form" >
So, your callback gets the event parameter and you pass this to your handler function with your other variables.
onFormSubmit(event, otherVar) {
event.preventDefault()
console.log(otherVar)
}
I have a react-redux app which requires a part which is conditional.
i.e. it can either be a div with a certain className or a div with the same className and an onClick handler which fires an action. so I created a function which returns jsx according to the above conditions.
Now the problem is that the onClick is not being added to the props as I expected but className is working fine.
class someClass {
renderSomething(){
return <div className="someClass" onClick={() =>
this.props.someAction()}>Something</div>
}
render(){
{this.renderSomething()}
}
}
What I expected it to be
<div className="someclass" onClick={() => this.props.someAction()}>Something</div>
What Rect dev tools show
<div className="someclass">Something</div>
Don't know where I went wrong.
Edit 1: The function was mistakenly written outside the class.
You have a typo. In your renderSomething() method, you should have the following:
renderSomething() {
return (
<div className="someclass" onClick={this.props.someAction}>Something</div>
);
}
EDIT: Now I saw that renderSomething was not a part of class from it was called from. In order to keep reference to this, you have to move it inside of your class, hence make it class method.
If you wish to add an argument to someAction function, I suggest you add a event handler to your React component, and then assign it to a onClick. Using arrow function in render method causes creation of a new function in memory every time component re-renders.
Maybe you meant to do this?
class someClass {
renderSomething(){
return <div className="someClass" onClick={() => this.props.someAction()}>Something</div>
}
render(){
{this.renderSomething()}
}
}
In your original example, renderSomething wasn't a part of someClass -- so calling this.renderSomething() would have thrown a runtime error.
I could be wrong, but i think
render(){
return {this.renderSomething()}
}
Is the way to go. Let me know if render(){this.renderSomething()} works.
I found out that my argument passed from parent to child component through refs is either undefined or an actual event instead of what I passed in.
What is the right way to actually pass argument from parent to child component's function? I am using refs but seems its not really passing anything in term of function argument.
I show the case here:
https://codepen.io/adamchenwei/pen/gLMaPo/?editors=0010
Basically if you see the line where I wrote console.log('child argument should be string'); it should be an argument actually passed instead of an event.
I wonder if I did things wrong.
For this scenario, I NEED to evaluate event in parent component and decide how I want to run the child function, with specific arguments passed in accordingly.
There are only two way to access parent props in child component. you can achieve this in following two ways:-
Pass values into child component from parent via props
Set values in state and dispatch action from child to fetch values from state.
Instead of using ref, I would use state and props:
parentDoThing(event) {
const lifeTerms = {
heaven: 'yeeeeey',
hell: 'nooooooo',
}
if(event) {
this.setState({ lifeTerms: heaven });
} else {
this.setState({ lifeTerms: hell});
}
}
render() {
return (
<section className="parent-comp"
onClick={this.parentDoThings}>
<ChildComponent
lifeTerms={this.state.lifeTerms}/>
<h1> Whats in App {this.state.text}</h1>
</section>
);}
And on child component:
render() {
return (
<section className="child">
<section>this life is {this.state.life} </section>
<button onClick={this.doChildThings.bind(this, this.props.lifeTerms)}> Button </button>
</section>
);
Resolved my issue by using componentWillUpdate in combine with using parent level state passed into child as props to trigger event inside the child, because I do not want any trigger inside the child to trigger the change provided by the parent.