React 中的模式(包装器)

Patterns in React (wrapper)

美好的一天。我正在构建一个组件树,并希望在树的其他组件中使用根组件的功能。我通过所有树抛出函数引用。 如果我需要从非根组件中的函数获取值,我也会使用该对象。 你能帮助我吗? 你能告诉我如何以 HOC 的身份执行此操作吗? 如果你在我的代码上展示例子就不那么难了。

import React from 'react';

class Page extends React.Component{

    Answer = {
        value : ''
    }

    doSomething(){

        console.log(this.Answer.value);
        console.log('Ready!');
    }
    
    render(){
        return(
            <div>
                <div>
                    <Body 
                        ParentFunc={()=>this.doSomething()} 
                        ParentParameters={this.Answer}
                    />
                </div>
            </div>
        )
    }
}

export default Page

class Body extends React.Component{
    render(){

        const{
            ParentFunc,
            ParentParameters
        } = this.props
        
        return(
            <div>
                <div>
                    <SomeComponent 
                        ParentFunc={()=>ParentFunc()}
                        ParentParameters={ParentParameters}
                    />
                </div>
            </div>
        )
    }
}

class SomeComponent extends React.Component{

    getAnswer(){
        
        const{
            ParentFunc,
            ParentParameters
        } = this.props

        ParentParameters.value = 'Some text'
        
        ParentFunc()
    }
    
    render(){
        
        return(
            <div onClick={()=>this.getAnswer()}>
                We can?
            </div>
        )
    }
}


我不认为仅凭高阶组件就能解决道具钻探的基本问题。 React Context 更适合为“想要在树的其他组件中使用根组件的功能”提供值和功能。

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

In a typical React application, data is passed top-down (parent to child) via props, but such usage can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.

首先创建您的 Context 和 Provider 组件:

const QnAContext = React.createContext({
  answer: {
    value: ""
  },
  doSomething: () => {}
});

const QnAProvider = ({ children }) => {
  const answer = {
    value: ""
  };

  const doSomething = () => {
    console.log(answer.value);
    console.log("Ready!");
  };

  return (
    <QnAContext.Provider value={{ answer, doSomething }}>
      {children}
    </QnAContext.Provider>
  );
};

在你的应用程序中渲染 QnAProvider 包裹你想要访问所提供的值的 React 子树:

<QnAProvider>
  <Page />
</QnAProvider>

使用上下文:

    基于
  • Class 的组件通过 render props 模式使用上下文。

     <QnAContext.Consumer>
       {({ answer, doSomething }) => (
         <SomeComponent doSomething={doSomething} answer={answer}>
           We can?
         </SomeComponent>
       )}
     </QnAContext.Consumer>
    
  • 函数式组件可以使用useContext React hook

     const SomeComponent = ({ children }) => {
       const { answer, doSomething } = useContext(QnAContext);
    
       getAnswer = () => {
         answer.value = "Some text";
    
         doSomething();
       };
    
       return <div onClick={this.getAnswer}>{children}</div>;
     };
    

这里是使用高阶组件可能有用的地方。您可以将 QnAContext.Consumer 渲染道具模式抽象为 HOC:

const withQnAContext = (Component) => (props) => (
  <QnAContext.Consumer>
    {(value) => <Component {...props} {...value} />}
  </QnAContext.Consumer>
);

然后您可以装饰要注入上下文值的组件。

const DecoratedSomeComponent = withQnAContext(SomeComponent);

...

<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>

注意:这样做的目的是将之前在Page中定义的值和函数移动到Context中,因此它们不再被传递从 PageBodySomeComponent(或 任何 其他子组件)。

演示

沙盒代码:

const QnAContext = React.createContext({
  answer: {
    value: ""
  },
  doSomething: () => {}
});

const QnAProvider = ({ children }) => {
  const answer = {
    value: ""
  };

  const doSomething = () => {
    console.log(answer.value);
    console.log("Ready!");
  };

  return (
    <QnAContext.Provider value={{ answer, doSomething }}>
      {children}
    </QnAContext.Provider>
  );
};

const withQnAContext = (Component) => (props) => (
  <QnAContext.Consumer>
    {(value) => <Component {...props} {...value} />}
  </QnAContext.Consumer>
);

class SomeComponent extends React.Component {
  getAnswer = () => {
    const { doSomething, answer } = this.props;

    answer.value = "Some text";

    doSomething();
  };

  render() {
    return (
      <button type="button" onClick={this.getAnswer}>
        {this.props.children}
      </button>
    );
  }
}

const DecoratedSomeComponent = withQnAContext(SomeComponent);

class Body extends React.Component {
  render() {
    return (
      <div>
        <div>
          <QnAContext.Consumer>
            {({ answer, doSomething }) => (
              <SomeComponent doSomething={doSomething} answer={answer}>
                We can?
              </SomeComponent>
            )}
          </QnAContext.Consumer>
        </div>
        <div>
          <DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
        </div>
      </div>
    );
  }
}

class Page extends React.Component {
  render() {
    return (
      <div>
        <div>
          <Body />
        </div>
      </div>
    );
  }
}

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <QnAProvider>
        <Page />
      </QnAProvider>
    </div>
  );
}

根据您当前的代码,我假设 Body 在将 ParentFuncParentParameters 的值传递给 SomeComponent 之前不会修改它们。

你有等级制度

<Page>
  <Body>
    <SomeComponent>
  </Body>
</Page>

并且您想将道具从 Page 传递到 SomeComponent 而无需经过 Body

您可以使用 children

childrena special prop 表示组件的 JSX 子元素。我们这样做是为了 Body 渲染它通过 props:

获得的 children
class Body extends React.Component{
    render() {
        return(
            <div>
                <div>
                    {this.props.children}
                </div>
            </div>
        )
    }
}

我们通过在 <Body>:

中使用 <SomeComponent/> 元素来设置 children 属性
render() {
  return (
    <div>
      <div>
        <Body>
          <SomeComponent
            ParentFunc={() => this.doSomething()}
            ParentParameters={this.Answer}
          />
        </Body>
      </div>
    </div>
  );
}

请注意,您不能直接修改从父级获得的值,这是您在 ParentParameters.value = 'Some text' 中所做的。如果你想更新父级的状态,那么你需要通过你的回调函数道具来做到这一点。所以你的代码应该是这样的:

import React from "react";

class Body extends React.Component {
  render() {
    return (
      <div>
        <div>{this.props.children}</div>
      </div>
    );
  }
}

class SomeComponent extends React.Component {
  state = {
    showAnswer: false
  };

  onClick() {
    // update answer in parent
    this.props.setAnswer("Some text");
    // change state to reveal answer
    this.setState({ showAnswer: true });
  }

  render() {
    return (
      <div>
        {this.state.showAnswer && <div>Answer is: {this.props.answer}</div>}
        <div onClick={() => this.onClick()}>We can?</div>
      </div>
    );
  }
}

class Page extends React.Component {
  state = {
    value: ""
  };

  render() {
    return (
      <div>
        <div>
          <Body>
            <SomeComponent
              answer={this.state.value}
              setAnswer={(answer) => this.setState({ value: answer })}
            />
          </Body>
        </div>
      </div>
    );
  }
}

export default Page;