使用 Context API 示例解释 React 高阶组件

Posted

技术标签:

【中文标题】使用 Context API 示例解释 React 高阶组件【英文标题】:React Higher Order Component explained with Context API example 【发布时间】:2019-02-10 17:34:52 【问题描述】:

我试图理解一个使用上下文 api 的高阶组件的示例,使用 here。

由于我对反应和函数式编程还很陌生,所以我不明白这里的实际情况如何。我们正在向函数withUser 发送一个函数,该函数返回另一个接收props 的函数。

function withUser(Component) 
  return function ConnectedComponent(props) 
    return (
      <UserContext.Consumer>
        user => <Component ...props user=user />
      </UserContext.Consumer>
    );
  ;

所以,当我们创建UserAvatar 时,我们传递接收props 并返回img 的函数。

const UserAvatar = withUser(( user, size ) => (
  <img
    className=`user-avatar $size || ""`
    
    src=user.avatar
  />
));

所以,基本上在这之后我们可以想象UserAvatar 是:

const UserAvatar = props => 
        return (
          <UserContext.Consumer>
            user => <Component ...props user=user />
          </UserContext.Consumer>
        );
      ;

Component 在哪里:

( user, size ) => (
      <img
        className=`user-avatar $size || ""`
        
        src=user.avatar
      />
    )

我希望到目前为止这是正确的,但是我不明白的是这一行:

user => <Component ...props user=user />

当我们已经拥有props 并且正在将userUserStats 组件向下传递给UserAvatar 组件时,为什么我们需要将其作为函数而不只是返回组件?

const UserStats = () => (
  <UserContext.Consumer>
    user => (
      <div className="user-stats">
        <div>
          <UserAvatar user=user />
          user.name
        </div>
        <div className="stats">
          <div>user.followers Followers</div>
          <div>Following user.following</div>
        </div>
      </div>
    )
  </UserContext.Consumer>
);

【问题讨论】:

UserAvatar 是父组件。 UserStats 不知道 UserAvatar 存在。 UserAvatar 需要知道传递了哪些组件,以便渲染子道具。 props 被传递给 UserAvatar,因为它是控制模块。 【参考方案1】:

也许最简单的方法是描述一个类似的概念:函数装饰器。

// Start with a function, any function.
let add = (a, b) => a + b;

但是现在我们要为参数添加日志并返回:

let addWithLogging = (a, b) => 
  console.log("Args are ", a, b);
  const result = a + b;
  console.log("Result is ", result);
  return result;

吐。我们简单的单行函数现在被一堆完全偶然添加两个数字的东西复杂化了。如果我们的大多数函数都很简单,并且我们想为所有函数添加日志记录,那么粗略的计算是我们的代码库将会翻倍。双杠。

但是等等,这是 javascript,我们有更高阶的函数并且可以提取一个装饰器:

// Here we take a function f and wrap it. We'll return a function that will
// collect the arguments, log them, perform f on them, log the result, and
// then finally return that result to the caller.
const withLogging = f => (...args) => 
  console.log("Args are ", ...args);
  const result = f(...args);
  console.log("Result is ", result);
  return result;
;

addWithLogging = withLogging(add);

React 对高阶组件使用相同的想法,您有一个组件需要一些额外的功能(经常状态和/或进行 AJAX 调用)。你不想让你漂亮的、简单的、可测试的纯功能组件复杂化,所以你使用更高阶的组件来代替。与上面的日志装饰器非常相似,示例中的高阶组件将组件作为参数,并返回一个匿名的纯函数式组件,该组件实际上接收道具并呈现包装在 UserContext 组件中的传入组件。

可以将 UserContext 组件作为其他组件返回的 JSX 的一部分:

const UserAvatar = ( user, size ) => (
  <UserContext.Consumer>
    <img
      className=`user-avatar $size || ""`
      
      src=user.avatar
    />
  </UserContext.Consumer>
);

但是现在,就像在日志记录示例中一样,您在每个需要它的组件中重复样板文件。

关于您引用的特定行,可以说您不需要那里的函数,因为据我所知,user 在呈现组件时已经在props 中。作为参考,这里是compiled JSX。

编辑

应该更仔细地阅读代码。

你需要一个函数的原因是因为它是一个上下文消费者。 user 通过上下文提供程序注入,唯一的方法是给它一个函数来传递参数。

【讨论】:

感谢函数装饰器的解释。我仍然想知道为什么我们需要使用我之前提到的行中的函数,我还认为我们已经在道具中拥有了用户。但是,如果我删除该功能并只留下 &lt;Component ...props /&gt; 沙盒示例就会崩溃。 @Leff 你能提供链接吗? @Leff 看到我更新的答案。抱歉花了这么长时间才回复您的问题,但我必须解决实际工作问题而不是堆栈溢出问题。

以上是关于使用 Context API 示例解释 React 高阶组件的主要内容,如果未能解决你的问题,请参考以下文章

React context API-我如何从 useEffect 钩子中分派一个动作?

新的 React Context API 会触发重新渲染吗?

React Context API和组件方法[重复]

React Context 详细介绍(状态共享数据传递)

React - 新的 Context API 不适用于 Class.contextType,但适用于 Context.Consumer

React API