1

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


4

2 回答 2

2

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

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

在典型的 React 应用程序中,数据通过 props 自上而下(父级到子级)传递,但对于应用程序中许多组件所需的某些类型的 props(例如语言环境偏好、UI 主题)来说,这种使用可能很麻烦。Context 提供了一种在组件之间共享此类值的方法,而无需显式地通过树的每一层传递一个 prop。

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

使用上下文:

  • 基于类的组件通过渲染道具模式使用上下文。

     <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通过BodySomeComponent(或任何其他子组件)传递。

演示

编辑模式反应包装

沙盒代码:

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>
  );
}
于 2021-06-04T16:48:29.143 回答
1

根据您当前的代码,我假设Body在将它们传递给.ParentFuncParentParametersSomeComponent

你有一个层次结构

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

并且您想将道具从 传递PageSomeComponent而不经过Body.

您可以使用children

children一个特殊的 prop,表示组件的 JSX 子元素。我们这样做是为了Body渲染 children它通过道具获得的:

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

我们children通过使用<SomeComponent/>内部的元素来设置该道具<Body>

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;
于 2021-06-04T18:39:49.607 回答