Vyhledávání na webu

reactjs - React TypeScript kontejnerový rodič předává vlastnosti dětem


Snažím se s tím několik hodin, ale nepodařilo se mi najít uspokojivé řešení. Chci mít tento obal, který obsahuje nějaký stav, který pak mohu buď předat jeho dítěti, nebo vykreslit něco jiného.

Chtěl bych udělat něco jako tento abstraktní příklad. Existuje něco v tomto duchu, co bych mohl udělat?

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>
}

Odpověď č. 1

Běžný vzor pro tento druh problému je přístup "Render Props". Objekt "state wrapper" bere prop, který předává jeho data něčemu jinému k vykreslení. Tímto způsobem nemusíte provádět žádné podivné měnění nebo kopírování dat stavu a názvy se nemusí nutně dokonale shodovat, což usnadňuje v budoucnu výměnu komponent.

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>
  );
}

Více informací: https://reactjs.org/docs/render-props.html

Odpověď č. 2

3 typy wrapperu s ref a přidanými vlastnostmi v TypeScriptu: 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