Reputation: 2129
What I try to acheive is NOT JUST render a component by onClick
but also pass a dynamic function to the component.
Restrictions
In the project there is Dialog component from the FluentUI. This dialog has open
property which is boolean and can be triggered by that. On one of the pages we lots of buttos which need to trigger the <Dialog/>
this is why I can't just paste this sample code and change open
state:
<Dialog
cancelButton={()=>{setOpen(false)}}
confirmButton={handleOnclick}
header="Action confirmation"
open={open}
/>
Requirements:
confirmButton
Simplified code
Here is the link to the sandbox. Here how it looks:
import React from "react";
export default function App() {
const [visible, setVis] = React.useState(false)
const Component = ({id,execute}:{id:number,execute?:any}) =>{
//this part here should stayed untouched
return visible ? <button onClick={execute(id)}></button> : <></>
}
const handleClick = (id: number,execute: any)=>{
setVis(true)
return <Component id={id} execute={execute}/>
}
return (
<div className="App" >
<button onClick={()=>handleClick(1,(id:number)=>{console.log(id)})}>First</button>
<button onClick={()=>handleClick(2,(id:number)=>{console.log(id)})}>Second</button>
</div>
);
}
The code tries to replicate my situation but more simple way. I'm trying to render a component which has a button to execute some code passed to it.
Problem: Component
never displays
Upvotes: 1
Views: 114
Reputation: 17714
EDIT: React docs discourages storing components in state - not giving a reason though in the docs I found. So it's probably better just to store { id, execute } in state an pass them along to the component when rendering.
Like this:
import React from "react";
export default function App() {
const [visible, setVis] = React.useState<any>()
const Component = ({id,execute}:{id:number,execute?:any}) =>{
//this part here should stayed untouched
return <button onClick={() => execute(id)}></button>
}
const handleClick = (id: number, execute: any)=>{
setVis({ id, execute })
}
return (
<div className="App" >
{ visible ? <Component id={visible.id} execute={visible.execute} /> : <div /> }
<button onClick={()=>handleClick(1,(id:number)=>{console.log(id)})}>First</button>
<button onClick={()=>handleClick(2,(id:number)=>{console.log(id)})}>Second</button>
</div>
);
}
First revision of answer below:
When you call handleClick
you return the component, you want rendered - but you don't actually render it. To render it you have to return the component as a part of the App-component's return value.
Like e.g.
return (
<div className="App" >
<Component /* missing arguments here */ />
<button onClick={()=>handleClick(1,(id:number)=>{console.log(id)})}>First</button>
<button onClick={()=>handleClick(2,(id:number)=>{console.log(id)})}>Second</button>
</div>
);
Now you have two different "types of visible" components: Two different id's with different callbacks dependent on which button was clicked originally. And you have to store this information in state
too. One way to do that is store these parameters as an object in state instead of just true/false.
But you can also just store the Component in state, like:
import React from "react";
export default function App() {
const [visible, setVis] = React.useState<any>()
const Component = ({id,execute}:{id:number,execute?:any}) =>{
//this part here should stayed untouched
return <button onClick={() => execute(id)}></button>
}
const handleClick = (id: number, execute: any)=>{
setVis(<Component id={id} execute={execute} />)
}
return (
<div className="App" >
{ visible ? visible : <div /> }
<button onClick={()=>handleClick(1,(id:number)=>{console.log(id)})}>First</button>
<button onClick={()=>handleClick(2,(id:number)=>{console.log(id)})}>Second</button>
</div>
);
}
Now I did have to change the part you wanted "untouched" for two reasons:
First of all, your onclick-callback was executed immediately upon rendering, so I wrapped it in a function. Secondly, the check for visible would always be true here, given that we just rendered the component - because it was truthy.
Upvotes: 2