【问题标题】:Patterns in React (wrapper)React 中的模式(包装器)
【发布时间】:2021-08-20 07:14:03
【问题描述】:

美好的一天。我正在构建一个组件树,并希望在树的其他组件中使用根组件的功能。我通过所有树抛出函数引用。 如果我需要从非根组件中的函数获取值,我也会使用该对象。 你能帮助我吗? 你能告诉我如何作为 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>
        )
    }
}


【问题讨论】:

  • 您到底想从什么中创建高阶组件? “想在树的其他组件中使用根组件的功能”听起来更像是您想创建一个上下文提供程序并提供通过上下文调用的函数。
  • 注意!不要使用上下文来避免将属性向下传递多个级别。真正的需求出现在必须在不同级别的许多组件中提供相同数据的情况下。
  • 那么,您想要创作哪些尚未创作的作品?您的问题不清楚。
  • 我明白了,如果您指的是the before you use context 部分,这只是说您可以将&lt;div onClick={()=&gt;this.getAnswer()}&gt;...&lt; 包含在一个道具中,然后将其传递给BodySomeComponent

标签: reactjs design-patterns react-hoc


【解决方案1】:

我不相信仅靠高阶组件就可以解决您的螺旋桨钻孔的基本问题。 React Context 更适合提供值和函数,通常是“想要在树的其他组件中使用根组件的函数”。

Context 提供了一种通过组件树传递数据的方法,而无需在每个级别手动向下传递 props。

在典型的 React 应用程序中,数据是自顶向下传递的(父级 child) 通过道具,但是对于某些类型,这种用法可能很麻烦 许多人需要的道具(例如语言环境偏好、UI 主题) 应用程序中的组件。上下文提供了一种共享方式 组件之间的此类值,而无需显式传递 贯穿树的每一层的道具。

首先创建 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>

使用上下文:

  • 基于类的组件通过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 中定义的值和函数移动到上下文中,因此它们不再从Page 传递到@987654336 @ 到 SomeComponent(或任何其他子组件)。

演示

沙盒代码:

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

【讨论】:

    【解决方案2】:

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

    你有一个层次结构

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

    并且您想将 props 从 Page 传递到 SomeComponent 而不经过 Body

    您可以使用children 执行此操作

    childrena special prop 代表组件的 JSX 子元素。我们这样做是为了让Body 渲染它通过道具获得的children

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

    我们通过在&lt;Body&gt; 中使用&lt;SomeComponent/&gt; 元素来设置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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      • 2016-12-19
      相关资源
      最近更新 更多