Passing event parameter when clicking a div in React - javascript

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)

Related

Use a function that was passed by props and is not associated with a given event as a click

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.

React component of grandparent callback doesnt re-render page after being call in grandchild

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>
);
}

My sort function is being called automatically upon render instead of onClick [duplicate]

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.

Higher Order React Component for Click Tracking

I'd like to implement a higher order react component which can be used to easily track events (like a click) on any React component. The purpose of this is to easily hook clicks (and other events) into our first party analytics tracker.
The challenge I've come across is that the React synthetic event system requires events (like onClick) be bound to react DOM elements, like a div. If the component I'm wrapping is a custom component, like every HOC implemented via a higher order function is, my click events don't bind correctly.
For example, using this HOC, the onClick handler will fire for button1, but not for button2.
// Higher Order Component
class Track extends React.Component {
onClick = (e) => {
myTracker.track(props.eventName);
}
render() {
return React.Children.map(
this.props.children,
c => React.cloneElement(c, {
onClick: this.onClick,
}),
);
}
}
function Wrapper(props) {
return props.children;
}
<Track eventName={'button 1 click'}>
<button>Button 1</button>
</Track>
<Track eventName={'button 2 click'}>
<Wrapper>
<button>Button 2</button>
</Wrapper>
</Track>
CodeSandbox with working example: https://codesandbox.io/embed/pp8r8oj717
My goal is to be able to use an HOF like this (optionally as a decorator) to track clicks on any React component definition.
export const withTracking = eventName => Component => props => {
return (
<Track eventName={eventName}>
{/* Component cannot have an onClick event attached to it */}
<Component {...props} />
</Track>
);
};
The only solution I can think of atm is using a Ref on each child and manually binding my click event once the Ref is populated.
Any ideas or other solutions are appreciated!
UPDATE:
Using the remapChildren technique from #estus' answer and a more manual way of rendering the wrapped components, I was able to get this to work as a higher order function - https://codesandbox.io/s/3rl9rn1om1
export const withTracking = eventName => Component => {
if (typeof Component.prototype.render !== "function") {
return props => <Track eventName={eventName}>{Component(props)}</Track>;
}
return class extends Component {
render = () => <Track eventName={eventName}>{super.render()}</Track>;
};
};
It's impossible to get a reference to underlying DOM node (and there can be more than one of them) from Wrapper React element by regular means, like a ref or ReactDOM.findDOMNode. In order to do that, element children should be traversed recursively.
An example:
remapChildren(children) {
const { onClick } = this;
return React.Children.map(
children,
child => {
if (typeof child.type === 'string') {
return React.cloneElement(child, { onClick });
} else if (React.Children.count(child.props.children)) {
return React.cloneElement(child, { children: this.remapChildren(
child.props.children
) });
}
}
);
}
render() {
return this.remapChildren(this.props.children);
}
remapChildren places onClick only on DOM elements. Notice that this implementation skips nested DOM elements.

Passing value into callback from Parent Component to Child without Binding

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)

Categories