how to pass two functions to child component - javascript

I'm trying to pass two functions yet not working. Here is my attempt
ChildComponent.js
render(
let val = -1
return(
<div>
<Button onClick={this.props.handelSecondClick} >second button click</Button>
<Button onClick={this.props.handelFirstClick} >First button click</Button>
</div>
)
)
App.js
this.state = {
data: [1,2]
}
handelFirstClick(val){
alert("first button clicked")
let data = this.state.data
data[0] = val
this.setState({data})
}
handelSecondClick(val){
alert("second button clicked")
let data = this.state.data
data[1] = val
this.setState({data})
}
render(
return(
< ChildComponent onClick={this.handelSecondClick(val)} onClick={this.handelFirstClick(val)}
)
/>
)
Is it even possible to pass two functions? On child component, I am creating two buttons two handle the two methods separetly. How should I modify so that both functions are passed two the child component?

Create 2 different functions.
Also you have missed out on binding these functions. Either use arrow functions or bind method.
< ChildComponent value={val}
handleSecondClick={() => this.handelSecondClick(val)}
handleFirstClick={()=> this.handelFirstClick(val)}
/>
You can access it via
this.props.handleSecondClick(this.props.value)
this.props.handleFirstClick(this.props.value)

Easy fix would be
ChildComponent.js
render(
return(
<div>
<Button onClick={this.props.onFirstClick} >second button click</Button>
<Button onClick={this.props.onSecondClick} >First button click</Button>
</div>
)
)
App.js
this.state = {
data: [1,2]
}
handelFirstClick(){
alert("first button clicked")
let data = this.state.data
data[0] = -1
this.setState({data})
}
handelSecondClick(){
alert("second button clicked")
let data = this.state.data
data[1] = -1
this.setState({data})
}
render(
return(
< ChildComponent onSecondClick={this.handelSecondClick} onFirstClick={this.handelFirstClick}
)
/>
)
Also this is recommended to do a check on the onclick, if the props is actually function or not, otherwise it will throw an error.
Try
onFirstClick = () => {
const {onFirstClick } = this.props;
if (typeof onFirstClick === 'function') {
onFirstClick();
}
}
render(
return(
<div>
<Button onClick={this.onFirstClick} >second button click</Button>
<Button onClick={this.onSecondClick} >First button click</Button>
</div>
)
)

You have to send 2 different props for handling multiple clicks. Do something like
<ChildComponent firstClick={this.handleFirstClick.bind(this, val)} secondClick={this.handleSecondClick.bind(this, val)} />
handleFirstClick(val){...}
handleSecondClick(val){...}
In your child component, access them as
render(){
return(
<Button onClick={this.props.firstClick}>Button 1</Button>
<Button onClick={this.props.secondClick}>Button 2</Button>
)
}

You should you object-destructuring
App.js
render() {
return(
< ChildComponent func1={this.handelSecondClick} func2={this.handelFirstClick} />
)
}
ChildComponent .js
render() {
const {func1, func2} = this.props
return(
<Button onClick={() => func1(val)} >second button click</Button>
<Button onClick={() => func2(val)} >First button click</Button>
)
}

There are two buttons in the component, you can pass both the functions as props in the component and add onClick method on buttons like:-
return(
<div>
<Button onClick={this.props.function2} >second button click</Button>
<Button onClick={this.props.function1} >First button click</Button>
</div>
)
and then in the component you can write as:-
< ChildComponent function1={this.handelFirstClick()} function2={this.handelSecondClick()} />
Hope this helps!

Related

In react, how to pass states down as props?

This is a very noob question but I've been trying all day do implement this. Please help me out.
Sorry for the length, just tried to put out the whole thing I am struggling with
I am trying to build custom buttons and to do so, I created a component so I can create as many buttons that I want. For that I declared a state and passed down some information as props, which is as follows:
import React, {useState} from 'react'
import Button from '../components/Button'
function CustomButton() {
const [clicked, setClicked] = useState(false)
return (
<div className='CustomButton'>
<Navbar />
<Button setClicked={setClicked} name="Button One" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Two" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Three" clicked={clicked}/>
</div>
)
}
export default CustomButton
As you can see, we passed the state and name of that button down. To render this Buttons, following component has been created:
import React from 'react'
import Modal from './Modal/Modal'
function Button({setClicked, name, clicked}) {
return (
<div>
<button onClick={() => {setClicked(true)}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
export default Button
And lastly, when once a button is clicked, we want to perform some action. That action is to pop the Modal on a screen. And to do so, we created a Modal and passed down few props. Code for the same is as follows:
import React from 'react'
function Modal({closeModal, name}) {
return (
<div className='modal'>
<div className='modalContainer'>
<p>{name}</p>
<div>
<button onClick={() => {closeModal(false)}}>×</button>
</div>
</div>
</div>
)
}
export default Modal
The expected result is for a Modal to pop with "You clicked button One", supposing we clicked one something similar to this.
The actual result is that all three Modals pop up one above the other when any of the three buttons are passed. The result:
I realize that I am passing the states wrong way. When any of the button is clicked all three get set to true. I simply don't realize how. Don't they create a method for each one?
Also, can you guys please teach me a better/understandable way to write clicked logic. Like maybe
if(clicked){
<Modal closeModal={setClicked} name={`You Clicked ${name}`} />
}
Because you bind all three buttons with one state, You need a state as array, with items equal to the number of buttons.
const [clicked, setClicked] = useState([false, false, false])
return (
<div className='CustomButton'>
<Navbar />
{
clicked.map((button, i) => {
return <Button setClicked={setClicked} name="Button Three" clicked={clicked[i]} index={i}/>
})
}
</div>
)
Then in the button component.
function Button({setClicked, name, clicked, index}) {
return (
<div>
<button onClick={() => {setClicked(prev => prev.map((item, i) => {
return i === index ? true : item
}))}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
And the modal component.
function Modal({ closeModal, name, index }) {
return (
<div className="modal">
<div className="modalContainer">
<p>{name}</p>
<div>
<button
onClick={() => {
closeModal((prev) =>
prev.map((item, i) => {
return i === index ? false : item;
})
);
}}
>
×
</button>
</div>
</div>
</div>
);
}
You can find a working example on this link.
https://codesandbox.io/s/old-wood-zgjno9
You can implement multiple modals like this:
import { useState } from "react";
export default function App() {
const [showModal1, setShowModal1] = useState(false);
const [showModal2, setShowModal2] = useState(false);
return (
<div className="App">
<button onClick={(e) => setShowModal1(true)}>Button 1</button>
<button onClick={(e) => setShowModal2(true)}>Button 2</button>
{showModal1 && (
<Modal text="Modal 1" onClose={(e) => setShowModal1(false)} />
)}
{showModal2 && (
<Modal text="Modal 2" onClose={(e) => setShowModal2(false)} />
)}
</div>
);
}
const Modal = ({ text, onClose }) => {
return (
<div>
{text}
<button onClick={onClose}>Close</button>
</div>
);
};
Working example

How can I return a React Component from a function and render it onClick?

I have an array of objects that I am looping through in my higher order component. On click I want to pass one of those objects to my component and render that component. The problem I am having is it's unclear how to return the component and have React update the render to show that component. I've read several Stackoverflow posts on this and the problem of putting the component in the html is it passes all the items in my loop to my component instead of just the one I need onClick.
The warning I'm seeing in my console is react_devtools_backend.js:3973 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> Which makes sense however, I'm not sure what the proper syntax is to solve this problem. Below is the relevant code.
const FormGroup = ({index}) => {
const renderSection = form => ( // I want to render this
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
);
const addSection = form => {
setAdditionalSection(prevState => !prevState);
console.log('form', form);
renderSection(form);
};
return (
<section>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{myForm.controls.map(form => {
if (form.type === 'section') {
return (
<FormSection>
<div className="section__label">
<h4>{form.label}</h4>
</div>
...
{form.button
&& (
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)} // called here and passes my form object
>
<span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
)}
{renderFormType(form)}
</FormSection>
);
}
})}
// renderSection should render here outside of the loop
{additionalSection && renderSection}
<input type="submit" />
</form>
</FormProvider>
</section>
);
Put it in state and just render.
const { extraSection, setExtraSection } = useState(null);
const addSection = form => {
setExtraSection(form);
};
return (
...
// renderSection should render here outside of the loop
{extraSection}
);
The problem with your original approach is that you were not saving that element anywhere. You called renderSection upon click, but that just called the function without storing or displaying that code anywhere.
Then in you render method, you again referenced rederSection. This is just a copy of the function, now without a parameter. It doesn't have any element as parameter, and it's not even called, so React is complaining that you're trying to render a function, instead of an element.
try it
const renderSection = form => (
return(
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
)
);
Just in case anyone else may be struggling with this. Thanks to szaman and this article. Turns out I need to pass the section in the loop, but just pass through the data that was clicked.
const addSection = form => {
console.log('form', form);
setAdditionalSection(form);
};
return (
<section>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
{myForm.controls.map(form => {
if (form.type === 'section') {
return (
<FormSection>
<div className="section__label">
<h4>{form.label}</h4>
</div>
...
{form.button
&& (
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)} // called here and passes my form object
>
<span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
)}
{additionalSection && additionalSection.position === ind && (
<AdditiveSection
form={additionalSection}
register={register}
errors={errors}
/>
)}
{renderFormType(form)}
</FormSection>
);
}
})}
<input type="submit" />
</form>
</FormProvider>
</section>
);

How to pass a property from a react component to its parent?

I am creating a "presentation" component with multiple sections, each rendered dynamically.
In the parent component which houses all the different children, I want the "next button" disabled for each part until a certain condition has been met. The button lives in the parent component.
This component does not pass the property:
Child one example:
export function ChildOne() {
const [condition, setCondition] = useState(false);
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import ChildOne, condition from "../child-one"
export default function Parent() {
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
I'm not sure how to pass the condition property from the child component so that I can use it in the parent component. In addition, is this methodology an anti-pattern? Can I conditionally make the button in the parent disabled based on values from the child component in another way?
Thank you.
try this way
child:
export function ChildOne({setCondition}) {
return (
<div>
<button onClick={() => setCondition(true)}>
hello world
</button>
</div>
);
}
Parent:
import {ChildOne} from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne setCondition={setCondition} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
You should use a state in parent component to control disabled for steps. It can use when you have other pages.
export default function Parent() {
const [condition, setCondition] = useState({});
const changeCondition = (val) => {
setCondition({...condition, [page]: val})
}
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne changeCondition={} />
)}
</div>
<button isDisabled={!condition[page]}>Next</button>
);
}
export function ChildOne({changeCondition}) {
return (
<div>
<button onClick={() => {changeCondition(true)}}>
hello world
</button>
</div>
);
}
You could pass the onClick fucntion as a props param.
Child
export function ChildOne({onClick}) {
return (
<div>
<button onClick={onClick}>
hello world
</button>
</div>
);
}
Parent
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne onClick={() => setCondition(true)} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
in your Parent component try this :
import ChildOne, condition from "../child-one"
export default function Parent() {
const [condition, setCondition] = useState(false);
const handleClick = () => setCondition(true)
return(
<div className="childRenderer">
{page == 1 && (
<ChildOne handleClick={handleClick} />
)}
</div>
<button isDisabled={condition}>Next</button>
);
}
and in use children :
export function ChildOne({handleClick}) {
return (
<div>
<button onClick={handleClick}>
hello world
</button>
</div>
);
}

Rendering component after onClick

I want to render my data after button click. I use the condition rendering in my component and create a boolean variable in state object. After button clicked variable is changed and (as I expected) my data was renderered. But nothing happens. I know that this is a basic mistake.
class SortingMyArray extends React.Component {
constructor(props) {
super(props)
this.state = {
addcomments: addcomments,
isClicked : false
}
// this.sortBy = this.sortBy.bind(this)
}
sortBy() {
let sortedComments = [...this.state.addcomments].sort((a,b) => new Date(b.date) - new Date(a.date));
this.setState({
addcomments: sortedComments,
isClicked : true
})
console.log(this.state.isClicked)
}
render(){
return(
<div>
<div>
<button
onClick={() => this.sortBy() }>AdditionalCommentaries
</button>
</div>
</div>
)
if (this.state.isClicked){
return(
<div>
<div>
<AddComments addcomments = {this.state.addcomments} />
</div>
</div>
)
}
}
}
export default SortingMyArray;
You need to restructure your render method since part of code is unreachable, also you don't need to use IF sentence, you can use logical operator && to ask if isClicked is true to return AddComments component.
You need to install eslinter in your code editor, this can you help to detect this type
errors or others
RENDER METHOD:
render() {
//You can destructuring object, in this case your state is the object
const { isClicked, addcomments } = this.state;
return (
<div>
<div>
<button onClick={() => this.sortBy()}>AdditionalCommentaries</button>
</div>
{isClicked && (
<div>
<AddComments addcomments={addcomments} />
</div>
)}
</div>
);
}
You should not have more than one return inside render method. Instead try this:
Note: There is no if-else statement in JSX but we use the one bellow
render(){
return(
<div>
<div>
<button
onClick={() => this.sortBy() }>AdditionalCommentaries
</button>
</div>
</div>
{
this.state.isClicked
&&
<div>
<div>
<AddComments addcomments = {this.state.addcomments} />
</div>
</div>
}
)
}
This part of your code is unreachable by the program since it is placed after return. You should not have two returns.
if (this.state.isClicked){
return (
<div>
<div>
<AddComments
addcomments={this.state.addcomments}
/>
</div>
</div>
)
}
Also, your conditional statement is not valid JSX. Your render method should look something like this
render() {
return (
<div>
<div>
<button onClick={() => this.sortBy()}>
AdditionalCommentaries
</button>
</div>
{this.state.isClicked && (
<div>
<div>
<AddComments
addcomment={this.state.addcomments}
/>
</div>
</div>
)}
</div>
)
}

React.js and ES2015 - Pass a method from parent to child

I'm trying to remove the child element(Note) when a user click the remove button. The remove method is on parent(Board) and I try to pass it to child thru props, but it is not working.
I try to use simple remove, this.remove - not defined remove, or this, this.remove.bind(this) nothing seems to work;location: eachNote(text,i) method
import React from 'react';
import ReactDOM from 'react-dom';
class Note extends React.Component{
constructor(props){
super(props);
this.state = {editing: false};
}
edit() {
this.setState({editing: true});
}
save() {
let val = this.refs.newText.value;
this.setState({editing: false});
}
renderNormal(){
return (
<div>
<p>{this.props.children} </p>
<button type="button" onClick={this.edit.bind(this)}>Edit</button>
<button type="button" onClick={this.hRemove.bind(this)}>Remove</button>
</div>
);
}
renderForm(){
return (
<div>
<textarea ref="newText" defaultValue={this.props.children}></textarea>
<button type="button" onClick={this.save.bind(this)}>Saved</button>
</div>
);
}
render() {
if(this.state.editing ==true ) {return this.renderForm();}
else {return this.renderNormal();}
}
}
class Board extends React.Component{
constructor(props) {
super(props);
this.state = {comments: ['icecream','antimoniu','bentrans'] };
}
remove(i){
let arr = this.state.comments;
arr.splice(i,1);
this.setState({comments: arr});
}
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove}>{text}</Note>);
}
render(){
return (
<div>
{this.state.comments.map(this.eachNote)}
</div>
);
}
}
ReactDOM.render(<Board />, document.getElementById('container'));
I tried Rafi Ud Daula Refat and Sven (thanks for answers) codes and the below one, but I still received the error: this is undefined;
in the Parent, I have:
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove.bind(i)}>{text} </Note>);
}
in the Child, I have:
removed(i) {
this.props.hRemove(i);
}
renderNormal(){
return (
<div>
<p>{this.props.children} </p>
<button type="button" onClick= {this.edit.bind(this)}>Edit</button>
<button type="button" onClick= {this.removed.bind(this,i)}>Remove</button>
</div>
);
}
I tried also this.removed.bind(this) and this.removed.bind(i), hRemove={this.remove.bind(i)}, and their combinations not working
If you want to use one method of parent you should pass the function as a props to the child. and from child you can access it as
this.props.functionName
Here in your note Component
<button type="button" onClick={this.hRemove.bind(this)}>Remove</button>
But note component does not have any method named hRemove. It can be assed through
this.props.hRemove()
<button type="button" onClick={this.props.hRemove(idorsomething)}>Remove</button>
And as the function 'remove' in the parent component has one parameter. So from the Child component note you have pass variable a. then it will work. like
this.props.hRemove(id)
When you pass the function remove as a hRemoveprop to your Note, you can find it in this.props.hRemove.
You can also bind the Note directly to the passed remove function:
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove.bind(i)}>{text}</Note>);
}
Then you simply use
<button type="button" onClick={this.props.hRemove}>Remove</button>
Keep in mind, with ES6 you don't have this in your custom render methods, this can be done in several ways: http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html

Categories