skip to Main Content

If I have a generic component that will supply some prop to an arbitrary child component at runtime like:

const GenericComponent = <T extends {id: string}>(props: {fetch: () => T, child: React.ReactElement}) => {
  const {id} = props.fetch()
  return (
    <div>{ props.child }</div>    // <------------- how to pass in argument here?
  )
}

with a child component that looks like this:

const PassedInComponent = (props: {id: string}) => {
  return (
    <div>{props.id}</div>
  )
}

What is the best way of achieving this?

From other Stackoverflow answers like this, I know I can pass in the child component with an empty id prop, then clone it with new arguments in the generic component:

<GenericComponent fetch={...} child={<PassedInComponent id={""} />} /> // <--- empty id prop passed in here


const GenericComponent = <T extends {id: string}>(props: {fetch: () => T, child: React.ReactElement}) => {
  const {id} = props.fetch()
  return (
    <div>{ React.cloneElement(props.child, {id: id}) }</div> // <---- can clone the element and pass in new arguments like this
  )
}

This example is pretty contrived but unfortunately I have to deal with this scenario due to other constraints.

My question is: supplying a child component with a dummy prop, then overwriting that props with ‘React.cloneElement’ seems pretty bad. Is there a better way of going about this?

2

Answers


    1. The correct way would be to create a utility function for the fetching, and do the fetch on the parent component, then finally pass it down to the children component
    2. There is an easier way to pass the child component, but it doesn’t apply to the solution to your problem.

    Here would be the ideal approach with explanation in the comments of the code

    // use whatever fetching library as you see fit
    // an utility function
    const getBatchData = axios.get('some.url')... // complete implementation here
    
    const GenericComponent = () => {
      const [fetchData, setFetchData] = useState()
      useEffect(() => {
        const doFetch = async () => {
          const result = await getBatchData() // fetch data in parent component
          setFetchData(result)
        }
        doFetch()
      }, [])
    
      if (!fetchData) return <></>
    
      // instead of passing `fetch` props, do it in parent and pass them to the children
      return (
        <div>
          {fetchData.map((data) => {
            return <PassedInComponent passInData={data} /> 
          })}
        </div> // assume fetchData
      )
    }
    

    In general, you want to call the component when the data is ready to be used for rendering UI, so there is less wasted rendering of the child components.

    Login or Signup to reply.
  1. You could just pass in the function component itself as the prop.

    const PassedInComponent: React.FC<{id: string}> = ({id}) => <div>{id}</div>;
    const GenericComponent = <T extends {id: string}>(props: {fetch: () => T, child: React.FC<T>}) => {
      const {id} = props.fetch(), ChildComp = props.child;
      return (
        <div><ChildComp id={id} /></div>
      )
    }
    

    Usage example:

    <GenericComponent fetch={...} child={PassedInComponent} />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search