I have a React component built like so, that takes in a callback function from its Parent component. When the onClick fires, it calls the callback with a value from the items being mapped over:
class Child extends Component {
static propTypes = {
...
}
render = () => {
return (
<div>
{this.props.data.map((el, idx) => {
return <section onClick={() = this.props.cb(el.val)}></section>
}
)}
</div>
);
}
}
What I'm wondering is how I can accomplish passing a value from that map to the callback without using this syntax () => this.props.cb(item.val) or binding a value to the callback. I can't just pass the callback to onClick because it fires immediately with the value, either.
The current syntax works but breaks the rules I have setup in my linter.
An alternative to binding in render is breaking out a new component which takes the callback and the value to be passed:
class Section extends React.Component {
handleClick = () => {
this.props.onClick(this.props.val)
}
render() {
return <section onClick={this.handleClick}></section>
}
}
(I would suggest just disabling that lint rule, though)
Related
My question is regarding the flow of an onClick event in React when clicking a div.
My application has a ParentComponent which calls in to a ChildComponent and subsequently creates a div for each member of the props.options array in the ParentComponent.
I have two questions which are as follows:
When I click the div, how can I pass the onClick event to this component?
How does the div know which option created it? I mean to ask, some iteration of the this.props.options array, say, optionA created, say, divA, how does divA know that it was created by optionA and not optionB? Is this done by React internally?
This is my Parent component
interface ParentComponentState {
..
..
}
export interface ParentComponentProps {
...
...
}
export class ParentComponent extends React.Component<ParentComponentProps, ParentComponentState> {
public state: ParentComponentState = {
...
};
constructor(props) {
super(props);
}
private handleClick = (item) => {
console.log(item);
}
public render(): JSX.Element {
return (
<>
<SomeButtonComponent>
{ this.props.options.map((item: any, index: any) => (
<ChildComponent
onClick={ () => this.handleClick(item) }
/>
)) }
</SomeButtonComponent>
</>
);
}
}
This is my Child component
export function ChildComponent(props: IChildComponent): JSX.Element {
return
(
<div
ref={ ... }
className={ someClassName }
dangerouslySetInnerHTML={ { __html: someHTML } }
onClick={ props.onClick }
/>
)
}
for your first question about passing event you can do this:
export class ParentComponent extends React.Component<ParentComponentProps, ParentComponentState> {
public state: ParentComponentState = {
...
};
constructor(props) {
super(props);
}
private handleClick = (item) => (event) => { // This is called function currying
console.log(item);
console.log(event);
}
public render(): JSX.Element {
return (
<>
<SomeButtonComponent>
{ this.props.options.map((item: any, index: any) => (
<ChildComponent
onClick={ this.handleClick(item) }
/>
)) }
</SomeButtonComponent>
</>
);
}
}
and for your second question if you want to know in ChildComponent that which options created it you have to pass another props to ChildComponent and send the option in ChildComponent as a prop and use it there
I would try to answer your questions to the best of my ability, so here it is:
Who controls the order of arguments in the invocation of this call?
You Do, React Only Says give me function which takes an argument and When an event for example click event happened I will call your function with and will send the event object as the parameter of it.
so it's on you, if you want to have more arguments you need to some how get around this by currying arguments or binding them like below:
handleClick = (item) => (event) => {
console.log(item);
console.log(event);
}
when you doing something like this and then in the onClick props you're calling the handle click like below:
<ChildComponent onClick={ this.handleClick(item) } />
you're actually sending
(event) => {
console.log(item);
console.log(event);
}
to your props and this is what React wants(A function which takes an argument and will call it when event happpens)
See Event Handling in React Docs
How does the browser know to pass in only the second argument as the actual event?
As I said above, React has its own Event System on top of browser Event System (Read about it here),
so the answer to this question is : it doesn't, React only needs a function with on parameter( For better understanding of the work around you need know about javascript closure in case you want to use currying or javascript bind function in case you're using binding )
Where is the contract for it?
you define in your code
Does the onClick function in the div expect a callback function where the second argument is the actual event?
No, It just need a function with one parameter, and in case it's confusing to you when using bind function, just read bout it a little here
Where is this defined?
it's one of the class parameter, and you need to read about it until you understand it
(I recommend using Function Components so you don't need to work with this concept in javascript)
Good morning community, I have a question. I am working with react js and I have to pass a function through props to a child component. The question is that this function is not going to be directly associated with a click, but I must use it but applying some logic in the child component and in case the condition is fulfilled launching said function passed by props. Can what I say be done or always the function that passes by itself must be associated with an arrow function and a given event?
export default father extends Component{
const fun = () => {
console.log("hello")
}
render(){
return(
<Child fun = {this.fun} />
)
}
export default child extends Component{
this.state = {
count : 1
}
const thing = () => {
const { count } = this.state
if(count == 1){
this.props.fun
}else{
console.log("is not 1")
}
}
render(){
return(
<Button onPress = {() => this.thing()} />
)
}
}
It's a pretty bad example but it's something similar to what I want to do
Yes, what you have written can be done. The function that you pass down as props can be executed conditionally, as an event handler, or not at all. Once you pass it as props it is completely up to you how you want to use it.
I am calling a handle method (to change state) in a <grandchild> component but it stop rendering after a couple of callback in the <grandparent> component.
I have tried to:
setting bind correctly with both this.bind in construct and arrow method.
making sure the call back is call everytime the prop.callback is call.
This is an example of what I'm trying to do with graphql server:
Grandparent Component
//Graphql constant for query
const ApolloConstant = gpl`
...etc
class Grandparent extends Component {
constructor(props) {
super(props);
this.state = { vars: 'query_string' }
}
handler = (args) => {
this.setState({vars: args})
}
render() {
return (
// For requerying graphql with search
<input onChange={() => this.setState(vars: e.target.value)} />
<Query variable={this.state.vars}>
...code -> some_data_arr
{<ChildComponent data={some_data_arr} handler={this.handler}/>}
</Query>
);
}
}
Child Component
//This component will take in an arr of obj and display a obj list
// if one of the obj is clicked then render a child component to display that single obj
class Child extends Component {
constructor(props) {
super(props);
this.state = {
singleData: null
}
}
render() {
return (
// Ternary operator here for conditional rendering
{
this.state.singleData
? <Grandchild data={this.state.singleData} handleParentData={this.props.handler} />
: this.display_data(this.props.data)
}
);
}
//Method to call to display objects
display_data = () => {
this.props.map() =>
<div onClick={this.setState({singleData: data})} > ...some code to display data <div/>
}
}
Grandchild Component
class Grandchild extends Component {
render() {
return (
{...do some code with object props here}
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
);
}
}
When I test this, everything works for the first 3-4 render then no more re-rendering even though the callback is going through. I check to see if the method in <grandparent> is being call and it does but the state stop changing. Even after going to a new route (react router) and then coming back, I still cant change state with that callback.
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
I think the problem is the function being called right into the onclick prop, you should probably have it wrapped in another function so it is only called when you actually trigger the onclick listener:
handleClick = () => {
this.props.handleParentData(vars)
}
render() {
return (
{...do some code with object props here}
<Button onclick={(this.handleClick)} >Btn</Button>
);
}
I have a component that I have created:
class Create extends Component {
constructor(props) {
super(props);
}
render() {
var playlistDOM = this.renderPlaylists(this.props.playlists);
return (
<div>
{playlistDOM}
</div>
)
}
activatePlaylist(playlistId) {
debugger;
}
renderPlaylists(playlists) {
return playlists.map(playlist => {
return <div key={playlist.playlist_id} onClick={this.activatePlaylist(playlist.playlist_id)}>{playlist.playlist_name}</div>
});
}
}
function mapStateToProps(state) {
return {
playlists: state.playlists
}
}
export default connect(mapStateToProps)(Create);
When I render this page, activatePlaylist is called for each playlist in my map. If I bind activatePlaylist like:
activatePlaylist.bind(this, playlist.playlist_id)
I can also use an anonymous function:
onClick={() => this.activatePlaylist(playlist.playlist_id)}
then it works as expected. Why does this happen?
You need pass to onClick reference to function, when you do like this activatePlaylist( .. ) you call function and pass to onClick value that returned from activatePlaylist. You can use one of these three options:
1. using .bind
activatePlaylist.bind(this, playlist.playlist_id)
2. using arrow function
onClick={ () => this.activatePlaylist(playlist.playlist_id) }
3. or return function from activatePlaylist
activatePlaylist(playlistId) {
return function () {
// you code
}
}
I know this post is a few years old already, but just to reference the latest React tutorial/documentation about this common mistake (I made it too) from https://reactjs.org/tutorial/tutorial.html:
Note
To save typing and avoid the confusing behavior of this, we will use
the arrow function syntax for event handlers here and further below:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => alert('click')}>
{this.props.value}
</button>
);
}
}
Notice how with onClick={() => alert('click')}, we’re passing a
function as the onClick prop. React will only call this function after
a click. Forgetting () => and writing onClick={alert('click')} is a
common mistake, and would fire the alert every time the component
re-renders.
This behaviour was documented when React announced the release of class based components.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html
Autobinding
React.createClass has a built-in magic feature that bound all methods to this automatically for you. This can be a little confusing for JavaScript developers that are not used to this feature in other classes, or it can be confusing when they move from React to other classes.
Therefore we decided not to have this built-in into React's class model. You can still explicitly prebind methods in your constructor if you want.
import React from 'react';
import { Page ,Navbar, Popup} from 'framework7-react';
class AssignmentDashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
}
onSelectList=(ProjectId)=>{
return(
console.log(ProjectId,"projectid")
)
}
render() {
return (
<li key={index} onClick={()=> this.onSelectList(item.ProjectId)}></li>
)}
The way you passing the method this.activatePlaylist(playlist.playlist_id), will call the method immediately. You should pass the reference of the method to the onClick event. Follow one of the below-mentioned implementation to resolve your problem.
1.
onClick={this.activatePlaylist.bind(this,playlist.playlist_id)}
Here bind property is used to create a reference of the this.activatePlaylist method by passing this context and argument playlist.playlist_id
2.
onClick={ (event) => { this.activatePlaylist.(playlist.playlist_id)}}
This will attach a function to the onClick event which will get triggered on user click action only. When this code exectues the this.activatePlaylist method will be called.
Essentially, I want to invoke a callback passed down from a parent component then reassign a value. I've tried creating a class method that invokes the given callback function from the parent components props, but I'm getting an infinite loop with setState. On another method that I tried but cannot seem to replicate at the moment, an error was thrown that stated "callback is not a function".
Perhaps, I'm phrasing this in a weird way. Here's an example:
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = { parentState: true }
this._handleToggle = this._handleToggle.bind(this);
}
_handleToggle() {
this.setState({ parentState: !this.state.parentState })
}
render() {
return (
<ChildComponent
onSomeEvent={this._handleToggle}
/>
)
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.randomInteger = 8;
this._invokeCallback = this._invokeCallback.bind(this);
}
// this is where I'm having trouble
_invokeCallback(callback) {
callback();
this.randomInteger = 0;
}
render() {
const { onSomeEvent } = this.props;
// Error
return (
<button onClick={this._invokeCallback(onSomeEvent)} />
)
}
}
What I want from here is to reassign this.randomInteger to a certain value AFTER invoking the callback that was handed down from the parent component. What can I do from here?
I apologize if my example is missing some pieces or is incomplete. I am rushing to write this up. Please feel free to correct any mistakes I made in my phrasing or example code. Thanks in advance.
Your _invokeCallback is executing immediately.
Due to the parentheses and passing an argument here this._invokeCallback(onSomeEvent), you are setting onClick to the result of the _invokeCallback method.
This is what is causing the infinite loop where setState in the parent causes a re-render in the child which then executes _invokeCallback again, and so on.
You could use an anonymous function with onClick so that _invokeCallback is only executed when the button is clicked:
render() {
const { onSomeEvent } = this.props
return (
<button onClick={ () => this._invokeCallback(onSomeEvent) } />
)
}
Alternatively, you could call the onSomeEvent function from the _invokeCallback method:
_invokeCallback() {
this.props.onSomeEvent()
this.randomInteger = 0
}
render() {
return (
<button onClick={ this._invokeCallback } />
)
}