branperr
branperr

Reputation: 514

React TypeScript containment parent passing props to children

I've been trying this for hours but I haven't found a satisfactory solution. I want to have this wrapper that contains some state that I can then either pass to its child or render something else.

I would like to do something like this abstract example. Is there anything along these lines that I can do?

const MyChild = (props:{state:boolean}) => {
   return <Text>`the state is ${props.state}`</Text>
}

const StateWrapper = ({children}:{children:React.ReactNode}) => {
   const hookState:boolean|null = useHookState()
   if (null) return <Loading />
   return {children} <-- with {state:boolean}
}

const App = () => {
   return <StateWrapper><MyChild /><StateWrapper>
}

Upvotes: 2

Views: 3342

Answers (2)

Robert P
Robert P

Reputation: 15968

A common pattern for this kind of problem is the "Render Props" approach. The "state wrapper" object takes a prop that passes its data to something else to render. This way you don't have to do any weird changing or copying of state data, and names don't necessarily have to align perfectly, making it easy to swap in other components in the future.

const MyChild = (props: {state: boolean}) => {
   return <Text>`the state is ${props.state}`</Text>
}

const StateWrapper = ({children}:{children: (state: boolean) => React.ReactNode}) => {
   const hookState:boolean|null = useHookState()
   if (null) return <Loading />
   return children(state);
}

const App = () => {
  return (
    <StateWrapper>
      {(state) => (<MyChild state={state}/>)}
    </StateWrapper>
  );
}

See more: https://reactjs.org/docs/render-props.html

Upvotes: 5

PeterT
PeterT

Reputation: 507

3 types of wrapper with ref and added props in typescript: Sandbox Demo:https://codesandbox.io/s/wrapper-2kn8oy?file=/src/Wrapper.tsx

import React, { cloneElement, forwardRef, useRef, useState  } from "react";
interface MyChild extends React.ReactElement {
    ref?:React.Ref<HTMLDivElement>|undefined
}
interface MyProps {
    children:MyChild|MyChild[],
    added:string,
}

const Wrapper1 = ({ children, added }: MyProps) => {
    const e =
        Array.isArray(children) ?
            children.map((child) => {
                return cloneElement(child, { added: added })
            }) :
            cloneElement(children)
    return <>
        {e}
    </>
}

const Wrapper2 = ({ children, added }:MyProps) => {

    const e =
        Array.isArray(children) ?
            children.map((child) => {
                return <child.type  {...child.props} added={added} ref={child.ref} />
            }) :
            children?<children.type {...children.props} added={added} ref={children.ref}/>:null
    //console.log("2:",e)
    if(!Array.isArray(children))console.log(children.ref)
    return <>
        {e}
    </>
}
const Wrapper3 = ({ children, added }:{children:any,added:any}) => {
    return <>
        {children(added)}
    </>
}

const Mydiv = forwardRef((props: any, ref?: any) =>
    <div ref={ref}>Origin:{props.message},Added:{props.added ?? "None"}</div>
)
const Container = () => {
    const ref1 = useRef<HTMLDivElement>(null)
    const ref2 = useRef<HTMLDivElement>(null)
    const ref3 = useRef<HTMLDivElement>(null)
    const [refresh, setRefresh] = useState(1)
    const setColor = () => {
        console.log("ref1:", ref1.current, "ref2:", ref2.current, "ref3:", ref3.current)
        if (ref1.current) ref1.current.style.color = "red"
        if (ref2.current) ref2.current.style.color = "red"
        if (ref3.current) ref3.current.style.color = "red"
    }

    return <>
        <button onClick={setColor}>Change Color</button>
        <button onClick={() => setRefresh((n) => n + 1)}>Refresh page</button>
        <div>{`state:${refresh}`}---state:{refresh}</div>

        <Wrapper1 added="Wrapper1 added">
            <Mydiv ref={ref1} message={`MyDiv 1 with Ref,parent state:${refresh}`} />
            <Mydiv message={`MyDiv 1 without ref,parent state:${refresh}`} />
        </Wrapper1>

        <Wrapper2 added="Wrapp2 added">
            <Mydiv ref={ref2} message={`MyDiv 2 with Ref,parent state:${refresh}`} />     
            <Mydiv message={`MyDiv 2 without ref,parent state:${refresh}`} />
        </Wrapper2>

        <Wrapper3 added="Wrapp3 added">
            {(added: any) => (<>
                <Mydiv ref={ref3} message={`MyDiv 3 with Ref,parent state:${refresh}`} added={added} />
                <Mydiv message={`MyDiv 3 without Ref,parent state:${refresh}`} added={added} />
            </>
            )}
        </Wrapper3>

    </>
}
export default Container

Upvotes: -1

Related Questions