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

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

Related

what if we accidently don't use '()' after calling the method

What if we forget put the parentheses after calling function, like when we have
renderContent(){}
and we want to call it in a div
<div>{this.renderContent()}</div>
what if we forget write ()?
it shows no error and shows nothing in the screen
and when we put () after calling, it shows if I got a problem like this how can I know where I didn't put ()
if you need more information please let me know
All that will happen in this specific case is it will print the function's code. So for example if you have a function like
renderContent = () => {
console.log("hi");
return <p>Hello world</p>;
}
Then using it without the parenthesis will just print the contents of the function, whereas using the parenthesis will print the JSX for the p tag <div>{this.renderContent()}</div>
will return <p>Hello world</p>
within the div, whereas without the parenthesis, it will just print
() => {
console.log("hi");
return <p>Hello world</p>;
}
which is the function's contents
import React, { Component } from 'react';
class Content extends Component {
renderContent = () => <h1>Hi</h1>
render() {
return (
<div>{this.renderContent()}</div>
)
}
}
export default Content;
This will show "Hi" in the screen.
But if you've deleted () from this.renderContent():
import React, { Component } from 'react';
class Content extends Component {
renderContent = () => <h1>Hi</h1>
render() {
return (
<div>{this.renderContent}</div>
)
}
}
export default Content;
It's showing nothing on the screen, and you would have an error in the console:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Because it supposed to return the structure of the function (if in vanilla javascript), but this is not allowed in React js

How to pass a props to event handler

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

Making a section change by clicking a button

i am making a website under react with reactstrap, i have a section that contains charts and a button whose function is to replace said charts with another chart containing more details. however i am struggling to make a concrete code.
i have tried placing the charts in a separate component and have it's content switch through the use of a handleclick function on the button that changes the state of the section (using 'onclick')
i am really not confident in my code's clarity, so i tried reproducing what i did in a simpler matter within fiddle
class hello extends React.Component {
render() {
return (
<h2>hello</h2>
);
}
}
class bye extends React.Component {
render() {
return (
<h2>goodbye</h2>
);
}
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<div>
<div>
{this.state.components[hello]}
</div>
<button onClick={this.handleClick}>
switch
{this.setState({components:[<bye />]})}
</button>
</div>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
the div in the "toggle" component is supposed to switch between the components "hello" and "bye"
in effect the current section that is supposed to be displayed ("hello") will be replaced by the other section ("bye") uppon clicking the button under them.
thanks in advance.
If you simply want to toggle between the two components with the button click, you can use conditional rendering.
Change your render method to this:
render(){
return (
<div>
{this.state.isToggleOn?<Hello />:<Bye />}
<button onClick={this.handleClick}>Switch</button>
</div>
}
Also keep your Component's name first character capitalized or react might complain. And using Class based Components is outdated. Hooks are the hot thing right now. So try to use more Functional Components.
Note: My answer assumes you are using babel presets for transpiling jsx and es6 syntax. If not, check out #Colin's answer. It also uses hooks.
why not import all partial views and conditionally render them based on the condition
{condition & <View1/>
There's a few mistakes in your code. Here's an example which does what you want using conditional rendering:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Hello = () => {
return <h2>hello</h2>;
};
const Bye = () => {
return <h2>bye</h2>;
};
const App = () => {
const [toggled, setToggled] = useState(true);
const handleClick = () => {
setToggled(!toggled);
};
const render = () => {
if (toggled) {
return <Hello />;
}
return <Bye />;
};
return (
<div>
<button onClick={handleClick}>toggle</button>
{render()}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
There are many ways to do it:
Using conditional operator:
{ this.state.isToggleOn?<Hello/>:<Bye/> }
Using if condition:
render() {
let chart;
if(this.state.isToggleOn) {
chart = <Hello/>;
} else {
chart = <Bye/>;
}
return ( <div> { chart } </div>);
}
3 You can use switch case also for conditional rendering. Here it is not well suited as condition is true or false.

easy react/babeljs question about returning props inside function

I'm learning a udemy course and in it we create this which is a function that takes a component and a class name as the arguments and returns a wrapped JSX having the WrappedComponent nested inside a <div>.
This is going to be real easy but I don't understand the syntax for props => (). Why do we use the props just after return statement? I understand that inside ( ) is the JSX to return. Maybe someone can easily explain why the props is there and how it gets handled?
import React from 'react';
const withClass = (WrappedComponent,className) => {
return props => (
<div className={className}>
<WrappedComponent/>
</div>
);
};
export default withClass;
The example what you copied is a common react pattern called HOC (Higher Order Component). What happens here is the following we have a function which takes a component as an argument ( WrappedComponent ) and we are returning a definition of a new component which will wrap our WrappedComponent. You could wrote the following as well
const withClass = (WrappedComponent,className) => {
return class extends React.Component {
render() {
return(
<div className={className}>
<WrappedComponent/>
</div>
)
}
}
};
So basically the syntax props => () is just a way to define a new component. It is worth to mention that the syntax itself is used to declare an arrow function.

How to pass callback from one child component to another

I have a main component, App, which has two child components, Player, and VideoList, where Player is a wrapper around react-player, heavily based off of the react-player demo.
Player has a method renderLoadButton() which creates a button that loads a particular video when clicked. I would like to have several of these buttons inside of my VideoList component.
I am attempting to pass the renderLoadButton() function up into the parent component, and then down into the VideoList component where I can call it.
Here is the code for render() function of the parent component. Both my <Player/> and <VideoList/> components instantiated here.
I get the following error on the line mentioned in the comment.
TypeError: Cannot read property 'renderLoadButton' of undefined
render() {
const dragHandlers = {onStart: this.onStart, onStop: this.onStop};
const {deltaPosition, controlledPosition} = this.state;
return (
<div className="App">
<div className="fullscreen">
<Draggable handle="strong" bounds={'body'}{...dragHandlers}>
<div style={{position: 'absolute', bottom: '30%', right: '50%'}} className="video-box no-cursor">
<Player ref={instance=>{this.player = instance}} title="VIDEO" url='https://streamable.com/nfec3'/>
</div>
</Draggable>
<Draggable handle="strong" bounds={'body'}{...dragHandlers}>
<div>
{/*Error on the following line*/}
<VideoList callback = {(x,y)=> this.player.renderLoadButton(x,y)}/>
</div>
</Draggable>
</div>
<div className="App-footer">
<img src={vinyl} className="App-logo" alt="logo" />
<h1>Radio</h1>
</div>
</div>
);
}
As per the code you provided you are doing it right i have created similar working model as yours it is working fine:
https://codesandbox.io/s/6y5p9woqq3
You can add your code to sandbox so that we will able to figure out what is the problem.
Edit
The Problem with your code is not index.js but is in VideoList.js as per your minimal code
VideoList.js:
import React, { Component } from "react";
class VideoList extends Component {
render() {
console.log("dd");
return this.props.callback('www.something.com','BUTTON');
}
}
export default VideoList;
Here you are trying to return a prop which contains a function not the original jsx for better clarity try console logging like this
console.log("dd",this.props.callback)
which shows a object returning your this.player.renderLoadButton function. so when you are trying to return it which returns just a function which cannot be rendered it is causing errors.
So if you have to pass that function which returns jsx don't use ref.Create a new obj or instance of Player class and extract the function from it and then pass it as prop to the videoList and the call it in render return.
so your App component should look like:
class App extends Component {
render() {
const obj = new Player
const func = obj.renderLoadButton
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Player title="VIDEO" url='https://streamable.com/nfec3'/>
<VideoList func={func} />
</div>
);
}
}
then your VideoList looks like:
class VideoList extends Component {
render() {
console.log("dd");
return (
<div>
{ this.props.func('www.something.com','BUTTON') }
</div>
)
}
}
export default VideoList;
here is working code :https://codesandbox.io/s/jpqnxwyyy
Edit 2:
i don't think it is possible that way. one thing you can do is use the same jsx every where and use the another function as props every where to call again. like this: https://codesandbox.io/s/7zwyl0yp3j
When use this.METHOD_NAME you must initial method!
constructor(props){
super(props);
this.renderLoadButton = this.renderLoadButton.bind(this);
}
renderLoadButton(x,y){
console.log(x,y); // for example
}
render(){
return(
...
<VideoList callback={this.renderLoadButton}/>
...
)
}
If you want to use static methods of other class, first import class then use static methods like this:
import Player from 'PLAYER_FILE_LOCATION';
.
.
.
.
.
render(){
return(
...
<VideoList callback={Player.renderLoadButton}/>
...
)
}

Categories