How to pass a props to event handler - javascript

I would like to pass a custom props to the onClick handler.
I cannot return an inline function as below, because I will have to later fire redux action creator as part of the handler (async action is not allowed)
onClick={()=>this.handleClick("v")}
Using middleware seems an overkill to me..
For simplicity purpose, please just ignore the redux part. Just say we can't use inline arrow function like this.
The example code below is just a POC approach that I borrow from input component, where value is an inherited props.
I am OK with any props("custom" as I said)
class Test extends React.Component {
handleClick = (event) => {
console.log(event.target.value);
}
render() {
return (
<div>
<label
value="v"
onClick={this.handleClick}
>
TEST Label
</label>
</div>
)
}
I expect console log to output a custom value -- "v"

your options are really limited. You should either use the inline arrow function and handle you async whatever problem in some other way or you should find a way to keep your state updated with current value of your label value. If it was an input onChange = {this.handleChange} would do it. this way your code will look like this:
handleClick(){
const {value} = this.state;
doSomething(value)
}
updateValue(input){
/* this.setState({
value : input
})*/
//in your case :
this.setState({
value : 'v'
})
}
render(){
return(
<label
value= {this.state.value}
onClick={this.handleClick}
>Text</label>
)
}
hope this helps

use mapDispatchToProps to pass action to the components and call it similar to above.
import React from "react";
import { action1 } from "./actions";
const App = ({action1})=> (
<button onClick={()=>action1("one")}>Click Me</button>
);
export default connect(null, {action1})(App);

Related

How to pass the 'onClick' function to child component in React

I create a very generic Modal that can get different header, body and footer, but also different arguments for their Reactstrap components (I'm using Reactstrap to create the Modal but the question needn't be specific to solve a Reactstrap problem).
My GenericModal.js code looks like:
class GenericModal extends React.Component{
render(){
return(
<Reactstrap.Modal {...this.props.other} />
<Reactstrap.ModalHeader {...this.props.headerArgs}>{this.props.header}</Reactstrap.ModalHeader>
<Reactstrap.ModalBody {...this.props.bodyArgs}>{this.props.body}</Reactstrap.ModalBody>
<Reactstrap.ModalFooter {...this.props.footerArgs}>{this.props.footer}</Reactstrap.ModalFooter>
</Reactstrap.Modal>);
}
}
And so I call this class like this:
<GenericCard {...{other: other, headerArgs: headerArgs, bodyArgs: bodyArgs, footerArgs: footerArgs,
cardheader:header, cardbody:body, cardfooter:footer}} />
Now I know that this method works because I've tried it with className, for example:
const bodyArgs = {className: 'my-5'};
I want to also be able to pass an onClick function - but not merely the function (as we can see in this question), but the whole thing: onClick=foo().
I'm having a bit of a problem understanding how I can put the onClick method inside a json-style format like I did with className.
I can't write an anonymous function for the onClick inside const bodyArgs = {...}, and writing it as
const bodyArgs = {onClick: {foo}};
Provides an undefined foo. I also can't put this.foo because it's an unexpected syntax as well.
Any thoughts?
Welp, found the solution moments after I posted this.
Just didn't need the {} curly brackets.
const bodyArgs = {onClick: this.foo};
Does the job.
Thought I'd keep it here in case anyone stumbles into this issue.
This should work as you have explained but I cannot fully know without the whole example. Here is a working bit of code and a codesandbox link of what you are tying to do.
import React from "react";
import "./styles.css";
class ClickExecutor extends React.Component {
render() {
return (
<div>
<h4>Click Executor</h4>
<div>
<button onClick={() => this.props.bodyArgs.alert1()}>One</button>
<button onClick={() => this.props.bodyArgs.alert2()}>Two</button>
</div>
</div>
);
}
}
class GenericModal extends React.Component {
alert1 = () => {
alert("Alert 1");
};
alert2 = () => {
alert("Alert 2");
};
render() {
const bodyArgs = {
alert1: this.alert1,
alert2: this.alert2
};
return (
<div>
<h1>Generic Modal</h1>
<ClickExecutor
{...{
bodyArgs: bodyArgs,
otherProps: "other various properties ..."
}}
/>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<GenericModal />
</div>
);
}
Working Demo LINK : https://codesandbox.io/s/smoosh-frost-rj1vb

Emitting custom event in React

In Vue.js we can emit custom events along with a parameter like
this.$emit('bark', 3);
and then we can listen to this event on the parent component like
<parent-component #bark=handleBark />
handleBark (howManyTimes) {
console.log(howManyTimes);
// logs 3
}
How can we do that in React?
As #usafder, mentioned the way. I am just adding the basic callback function for an input field. So on the console you can see the current value.
Basically callback function is the way to get the data from the Child component.
Parent.js
import React from "react";
import Child from "./Child";
export default function App() {
const parentHandleChange = (e) => {
console.log(e.target.value);
};
return (
<div>
<Child handleChange={parentHandleChange} />
</div>
);
}
Child.js
import React from "react";
const Child = (props) => {
return <input onChange={props.handleChange} />;
};
export default Child;
Working codesandbox
Addition to it if you need return a custom value use like this
<Child onHandleChange={() => parentHandleChange(10)}
Because in this it won't call every-time if you want pass a value.
You just simply pass down the custom event handler as props.
For example if you have Parent and Child functional components. You can then define the custom event handler in the Parent component like:
function Parent(props) {
const handleBark = (howManyTimes) => {
console.log(howManyTimes);
};
// note below I am sending the handleBark method to Child as props
return (<Child bark={handleBark} />);
}
and then inside the Child component you can simply call it as:
props.bark(10);
You can also use this library, Evento, that I created to replicate Svelte's createEventDispatcher() and Vue's $emit.
You have to create the event emitter (named by convention evento) using the hook, and use the dispatcher as you would do with $emit by passing the name of the event and the payload :
const Dog = (props) => {
const evento = useCreateEvento(props)
return <button onCLick={() => evento('bark', 3)}>wof</button>
}
The parent Component will be able to listen to the Event as it would listen to a React Event: by using on + the capitalized name of the Component Event. The data will be stored in event.detail.
<Dog onBark={e => console.log(`barked ${e.detail} times`)} />
/* will log 'barked 3 times'*/

Is this a valid way of updating state that depends on prevState?

I am following along with a video tutorial on using React. The presenter is currently detailing how to add a toggle button to a UI. They said to give it a go first before seeing how they do it, so I implemented it myself. My implementation was a little different to theirs, just the handler was different; but it does seem to work.
Can anyone with more experience using React tell me, is my toggleSideDrawerHandler wrong in some way? Or is it a valid shorter way of setting the state that depends on a previous state?
My implementation:
//Layout.js
class Layout extends Component {
state = {
showSideDrawer: false
};
toggleSideDrawerHandler = prevState => {
let newState = !prevState.showSideDrawer;
this.setState({ showSideDrawer: newState });
};
closeSideDrawerHandler = () => {
this.setState({ showSideDrawer: false });
};
render() {
return (
<Fragment>
<Toolbar drawerToggleClicked={this.toggleSideDrawerHandler} />
<SideDrawer
open={this.state.showSideDrawer}
close={this.closeSideDrawerHandler}
/>
<main className={styles.Content}>{this.props.children}</main>
</Fragment>
);
}
}
//Toolbar.js
const toolbar = props => (
<header className={styles.Toolbar}>
<DrawerToggle clicked={props.drawerToggleClicked} />
<div className={styles.Logo}>
<Logo />
</div>
<nav className={styles.DesktopOnly}>
<NavItems />
</nav>
</header>
);
Tutorial implementation:
toggleSideDrawerHandler = () => {
this.setState(prevState => {
return { showSideDrawer: !prevState.showSideDrawer };
});
};
Your solution works, but I guess in the part, where you call the toggleSideDrawerHandler you probably call it like
() => this.toggleSideDrawerHandler(this.state)
right?
If not, can you please paste the rest of your code (especially the calling part) to see where you get the prevState from?
This works, because you pass the old state to the method.
I would personally prefer the tutorials implementation, because it takes care of dependencies and the "user" (the dev using it) doesn't need to know anything about the expected data.
With the second implementation all you need to do is call the function and not think about getting and passing the old state to it.
Update after adding the rest of the code:
I think the reason, why it works is because the default value for your parameter is the one passed by the event by default, which is an event object.
If you use prevState.showSideDrawer you are calling an unknown element on this event object, that will be null.
Now if you use !prevState.showSideDrawer, you are actually defining it as !null (inverted null/false), which will be true.
This is why it probably works.
Maybe try to toggle your code twice, by showing and hiding it again.
Showing it will probably work, but hiding it again will not.
This is why the other code is correct.
You should stick to the tutorial implementation. There is no point in passing component state to the children and then from them back to the parents. Your state should be only in one place (in this case in Layout).
Child components should be only given access to the information they need which in this case is just showSideDrawer.
You are using this:
toggleSideDrawerHandler = prevState => {
let newState = !prevState.showSideDrawer;
this.setState({ showSideDrawer: newState });
};
This is a conventional way to update state in react, where we are defining the function and updating state inside. Though you are using term prevState but it doesn't holds any value of components states. When you call toggleSideDrawerHandler method you have to pass value and prevState will hold that value. The other case as tutorial is using:
toggleSideDrawerHandler = () => {
this.setState(prevState => {
return { showSideDrawer: !prevState.showSideDrawer };
});
};
This is called functional setStae way of updating state. In this function is used in setState methods first argument. So prevState will have a value equal to all the states in the component.Check the example below to understand the difference between two:
// Example stateless functional component
const SFC = props => (
<div>{props.label}</div>
);
// Example class component
class Thingy extends React.Component {
constructor() {
super();
this.state = {
temp: [],
};
}
componentDidMount(){
this.setState({temp: this.state.temp.concat('a')})
this.setState({temp: this.state.temp.concat('b')})
this.setState({temp: this.state.temp.concat('c')})
this.setState({temp: this.state.temp.concat('d')})
this.setState(prevState => ({temp: prevState.temp.concat('e')}))
this.setState(prevState => ({temp: prevState.temp.concat('f')}))
this.setState(prevState => ({temp: prevState.temp.concat('g')}))
}
render() {
const {title} = this.props;
const {temp} = this.state;
return (
<div>
<div>{title}</div>
<SFC label="I'm the SFC inside the Thingy" />
{ temp.map(value => ( <div>Concating {value}</div> )) }
</div>
);
}
}
// Render it
ReactDOM.render(
<Thingy title="I'm the thingy" />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
So depending on requirement you will use one of the two ways to update the state.

React multiple events attachment as jsx expression

Hi I have a query about JSX syntax support, will it be possible to give jsx expressions inside js object?
I have a Button component like this,
import React, {Component} from 'react';
export default function ButtonComponent(props){
return (
<button id={props.id} type="button" onClick={props.onClick} onMouseDown={props.onMouseDown} >
{props.value}
</button>
)
}
It will be used in some other component like bellow,
<ButtonComponent id="first" value="First" onClick={this.clickHandler} onMouseDown={this.mouseDownHandler}/>
As you can observe there is an array pattern getting created for events. It might grow with onMouseOver, onMouseUp etc.. events.
So I was just wondering if I could pass all the events as one object and attach them with spread operator?
Like I will modify the ButtonComponent as,
import React, {Component} from 'react';
export default function ButtonComponent(props){
return (
<button id={props.id} type="button" {...props.events} >
{props.value}
</button>
)
}
And use this component like below,
<ButtonClass id="first" value="First"
events={{onClick:{this.clickHandler}, onMouseDown:{this.mouseDownHandler}, onMouseOver:{this.mouseOverHandler}}}/>
But it throws error saying invalid syntax.
If I update the events object like below, it passes the syntax validation but events does not execute.
<ButtonClass id="first" value="First"
events={{onClick:(this.clickHandler), onMouseDown:(this.mouseDownHandler), onMouseOver:(this.mouseOverHandler)}}/>
Is there and way to achieve this pattern with out attaching all the events in the ButtonComponent?
Sorry for the long post..
You can define event listeners like an object and pass it to component with rest-spread operator. For example
const EVENTS = {
onMouseOver: () => console.log("mouseover"),
onMouseOut: () => console.log("mouseout"),
onClick: () => console.log("click"),
...
}
class MyCoolComponent extends React.Component {
render() {
return <button {...this.props.events}>{this.props.children}</button>
}
}
ReactDOM.render(
<MyCoolComponent events={EVENTS}>Some children</MyCoolComponent>,
document.body
)
However, this is not very convenient, because you always can mistype the event prop name (e.g. onCLick or something) and get the error in runtime. But it is possible :)

Modularizing code in React/Redux

The main question
I am used to using React with ES6 classes. I am also used to modularizing portions of code into separate functions. I am looking at the following example and trying to figure out how to put the value for onSubmit as a separate function.
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form
onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
I have tried something like this:
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
function handleSubmit(e){
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => handleSubmit(e)}>
<input ref={node => {input = node }}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
But then of course it does not work as it does not recognize the input variable. I could pass the input variable to the function, but this does not seem like the right way to do it.
Question 2:
I am unfamiliar with what the following piece of code is doing:
let AddTodo = ({ dispatch }) => {
Where exactly is it getting dispatch from? Is the value of dispatch being passed into the anonymous function?
Question 3
The same with the following code:
<input ref={node => {input = node }}
Where is the value of node coming from and why is it being stored into the input variable?
Answer to Question 1
AddTodo is a React stateless functional component (SFC). It is also a function. Within the SFC is defined a variable input. In order for the handleSubmit callback to be able to make use of input, it is necessary that input be in the enclosing scope where handleSubmit is defined or input be passed as an argument to handleSubmit.
Thus, the following two implementations achieve the desired behavior:
const AddTodo = ({dispatch}) => {
let input
const handleSubmit = e => {
...
}
return (
...
onSubmit={handleSubmit}
...
)
and
const handleSubmit = (e, input) => {
...
}
const AddTodo = ({dispatch}) => {
let input
return (
...
onSubmit={e => handleSubmit(e, input)}
...
)
I highly recommend reading the following blog post by Kent Dodds, paying particular attention to the use of classes vs function closures.
Classes, Complexity, and Functional Programming
Answer to Question 2
The connect function from react-redux wraps the AddTodo component. The way in which connect is being called (with no second argument, or any arguments in this particular case) means AddTodo will receive a prop named dispatch.
To better understand how react-redux and the connect function it provides work, have a look at the documentation:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
Answer to Question 3
Refs are built into React. A function passed to the ref prop receives the underlying DOM element as an argument. In this case, the function passed to the ref prop stores a reference to the DOM element in the variable input. This allows the DOM element to be accessed and mutated later by the callback passed to onSubmit (i.e. handleSubmit). See the React documentation for more details on refs:
https://reactjs.org/docs/refs-and-the-dom.html

Categories