React TS - 如何将 props 从父组件传递给深度嵌套的子组件

IT技术 reactjs typescript
2021-05-24 18:11:28

目前,我已经创建了一些不同的表单元素。我正在尝试以不同的方式创建它们,以便我可以将它们拼凑在一起为表单创建不同的格式。

以下是我的一些表单元素:

// Field Component
interface IField extends ILabel {}

export const Field: React.FunctionComponent<IField> = props => {
  return (
    <div>
      <Label {...props} />
      {props.children}
    </div>
  );
};

// Label Component
interface ILabel {
  htmlFor: string;
  label: string;
  required?: boolean;
}

export const Label: React.FunctionComponent<ILabel> = props => {
  return (
    <label htmlFor={props.htmlFor}>
      {props.label}
      // Some required icon would go where I've added the <span />.
      {props.required && <span />}
    </label>
  );
};

// Input Wrapper Component
export const InputWrapper: React.FunctionComponent = props => {
  return <div>{props.children}</div>;
};

// Input Component
interface IInput {
  type: string;
  id: string;
  name: string;
  value?: string;
  placeholder?: string;
  required?: boolean;
}

export const Input: React.FunctionComponent<IInput> = props => {
  return (
    <input
      type={props.type}
      id={props.id}
      name={props.name}
      value={props.value}
      placeholder={props.placeholder}
      required={props.required}
    />
  );
};

这是我实现组件的方式:

<Field htmlFor="name" label="Name:" required>
  <InputWrapper>
    <Input
      id="name"
      type="text"
      name="name"
      placeholder="Enter your name..."
    />
  </InputWrapper>
</Field>

我希望能够requiredField组件设置prop并且它也会传递给我的Input组件,无论它嵌套多深。这怎么可能?

我还提供了一个CodeSandBox演示。

提前感谢您的任何帮助!

1个回答

当使用props.children并且不可能知道嵌套组件的确切层次结构,或者它可能因不同的用例而异时,可能很难尝试props从父级传递到子级。

上下文为组件树提供了一种共享数据和对更改做出react的方法。

您的代码和框适用于解释的解决方案
https://codesandbox.io/s/react-stackoverflow-60241936-fvkdo

在您的情况下,您需要Context为您的Field组件创建一个“共享”required字段并定义默认值。

interface IFieldContext {
  required?: boolean;
}

export const FieldContext = React.createContext<IFieldContext>({
  required: false // default value when the prop is not provided
});

然后FieldContext.ProviderField组件中使用您可以为嵌套组件分配“共享”值。

export const Field: React.FunctionComponent<IField> = props => {
  return (
    <FieldContext.Provider value={{ required: props.required }}>
      <Label {...props} />
      {props.children}
    </FieldContext.Provider>
  );
};

最后,在Input组件中,用于FieldContext.Consumer访问“共享”值并检索required分配给Field组件prop 然后您将能够requiredIInput界面中删除该字段,因为它现在来自Context而不是来自props

interface IInput {
  type: string;
  id: string;
  name: string;
  value?: string;
  placeholder?: string;
}

export const Input: React.FunctionComponent<IInput> = props => {
  return (
    <FieldContext.Consumer>
      {context => (
        <input
          type={props.type}
          id={props.id}
          name={props.name}
          value={props.value}
          placeholder={props.placeholder}
          required={context.required} // access context prop
        />
      )}
    </FieldContext.Consumer>
  );
};

“瞧”,你可以requiredField组件使用prop ,它会被应用到你的嵌套Input组件上,无论Input嵌套多深......花哨的东西😄

export const App: React.FunctionComponent = () => {
  return (
    <Field htmlFor="name" label="Name:" required>
      <InputWrapper>
        <Input
          id="name"
          type="text"
          name="name"
          placeholder="Enter your name..."
        />
      </InputWrapper>
    </Field>
  );
};