1

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:

  • Dialog should be dynamic
  • Must only be rendered when we press on some buttons
  • Must execute function which is passed to it with 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

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. Could you spread more for "One way to do that is store these parameters as an object in state instead of just true/false." with example?
Sure, i have edited it in. It's a better solution :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.