Reputation: 514
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
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
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