使用 Recompose 在 React 组件之间共享功能

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

在 React 组件之间共享功能是一个非常普遍的需求。 这个概念是,我们可以在一个地方建立行为,然后将其扩展到不同的组件。 高阶组件 是实现此目的的一种方式。 但是,还有另一种方法可以使用一个名为 Recompose 的库。

GitHub 仓库

什么是 Recompose?

文档 有助于我们回答这个问题

Recompose 是一个 React 实用工具带,用于函数组件和高阶组件。 可以将其视为 React 的 lodash。

基本上,它是一个 React 库,包含一堆助手,这些助手返回不同的高阶组件——这很好,因为它减少了定义常见 React 模式的繁琐工作,并使它们立即可用以扩展到其他组件。

它到底能做什么? 好吧,让我们一起浏览几个示例。

向函数无状态组件添加状态

顾名思义,函数无状态组件没有任何状态。 它只是接受 props 并将 UI 返回到前端。

const Greeting = props =>
  <p>
    Hello, {props.name}!
  </p>

在您想在函数无状态组件中使用状态的场景中,您必须将其转换为类组件。 这正是 Recompose 发挥作用的地方。

Recompose 为您提供了 withState() 助手,以便向函数无状态组件添加状态。 它管理单个状态值。 您无法像在类组件中那样在 withState() 中管理多个状态值。 您传递一个函数来更新状态值,并传递一个可选的默认状态值。

以下是 withState() 的结构

withState(
  stateName: string, // the name we call our state
  stateUpdaterName: string, // the name of the function to call
  initialState: any | (props: Object) => any // optional default state to pass
): HigherOrderComponent

计数器是一个常用的示例,用于演示概念。 以下是我们如何使用 Recompose 的 withState() 助手来创建一个超简单的计数器

const App = withState("count", "handleCounter", 0)(({ count, handleCounter }) => {
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => handleCounter(n => n + 1)}>Increment</button>
      <button onClick={() => handleCounter(n => n - 1)}>Decrement</button>
    </div>
  );
});

由于 withState() 助手已经对我们可用,我们可以立即调用它并为其提供所需的参数。 再次强调,它们是

  • stateName:我们调用状态的名称
  • stateUpdaterName:要调用的函数的名称
  • initialState:要传递的可选默认状态

这些参数然后被集成到我们想要在前端渲染的 UI 标记中。

我们还可以通过另一种方式创建计数器组件,值得一看,以便更多地练习使用 Recompose 助手。

首先,我们使用 withState() 和所需的参数创建一个高阶组件。

const enhanced = withState("counter", "handleCounter", 0);

接下来,我们创建 Counter 组件,并在其中使用 withState() 参数

const Counter = ({ counter, handleCounter }) => (
  <div>
    <h1>{counter}</h1>
    <button onClick={() => handleCounter(n => n + 1)}>Increment</button>
    <button onClick={() => handleCounter(n => n - 1)}>Decrement</button>
  </div>
);

请注意,状态和更新函数的名称作为 props 传递给 Counter 组件。

最后,我们通过将 Counter 组件包装在增强的高阶组件中来创建 App 组件。

const App = enhanced(Counter);

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Recompose withState

这是另一种流行的方法

const enhanced = withState("count", "handleCounter", 0);
const App = enhanced(({ count, handleCounter }) => {
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => handleCounter(n => n + 1)}>Increment</button>
      <button onClick={() => handleCounter(n => n - 1)}>Decrement</button>
    </div>
  );
});

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Recompose withState v2

使用 withHandlers() 处理状态

Recompose 还具有 withHandlers() 助手,它允许您通过定义用于更新组件状态的函数来处理状态。 而且,您可以将其与 withState() 一起使用!

这些基本上是高阶函数,它们接收 props 并返回函数处理程序。 让我们像在前面的示例中一样分解结构。

withHandlers({
  incrementCounter: props => event => {
    event.preventDefault();
    props.handleCounter(props.count + 1);
  }
})

首先,我们确定了 incrementCounter,它将对我们的 Counter 组件可用,以便在单击时更新计数值。

接下来,我们像以前一样构造计数器——作为使用 withState() 的高阶组件

const enhancedState = withState("count", "handleCounter", 0);

现在,我们定义我们的函数并使用 withHandlers()

const enhancedHandler = withHandlers({
  incrementCounter: props => event => {
    event.preventDefault();
    props.handleCounter(props.count + 1);
  },
  decrementCounter: props => event => {
    event.preventDefault();
    props.handleCounter(props.count - 1);
  }
});

我们构造了一个我们称之为 enhancedHandler 的高阶组件,并在其中使用 withState() 定义了两个函数处理程序:incrementCounterdecrementCounter。 这些处理程序包含将作为 props 由调用它们的组件接收的参数。 如果我们想更新使用 withState() 定义的组件的状态,则需要它们。

好的,现在开始创建我们的计数器组件

const Counter = ({ count, incrementCounter, decrementCounter }) => (
  <div>
    <h1>{count}</h1>
    <button onClick={incrementCounter}>Increment</button>
    <button onClick={decrementCounter}>Decrement</button>
  </div>
);

看到了吗? 预计将状态和处理程序作为 props 传递给计数器组件。

要使用我们定义的高阶组件,我们必须将 Counter 组件传递给 enhancedHandler,并将该组件作为参数包装到 enhancedState 中。

换句话说

const App = enhancedState(enhancedHandler(Counter));

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Recompose withState & withHandlers

组合多个高阶组件

在最后一个示例中,我们使用了两个高阶组件。 有没有更好的方法将它们链接在一起? 绝对有! Recompose 为我们提供了 compose() 助手来实现这一点。 我们可以使用 compose() 创建一个组件,该组件将两个高阶组件在一个步骤中组合在一起。

const enhanced = compose(
  withState("count", "handleCounter", 0),
  withHandlers({
    incrementCounter: props => event => {
      event.preventDefault();
      props.handleCounter(props.count + 1);
    },
    decrementCounter: props => event => {
      event.preventDefault();
      props.handleCounter(props.count - 1);
    }
  })
);

现在,我们可以在 App 组件中使用增强组件

const App = enhanced(({ count, incrementCounter, decrementCounter }) => {
  return (
    <div>
      <p>{count}</p>
      <button onClick={incrementCounter}>Increment</button>
      <button onClick={decrementCounter}>Decrement</button>
    </div>
  );
});

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Recompose – compose withState & withHandlers

使用类似 Redux 的 reducer 管理状态

Recompose 的另一个优点是允许您使用 reducer 函数 (withReducer) 来管理状态。 reducer 会响应特定操作来更新组件的状态。

withReducer() 的结构如下所示

withReducer<S, A>(
  stateName: string,
  dispatchName: string,
  reducer: (state: S, action: A) => S,
  initialState: S | (ownerProps: Object) => S
): HigherOrderComponent</S>

第一个参数是状态的名称。 第二个是 dispatch 方法。 dispatch 将用于分派操作,就像我们在 Redux 中所做的那样。 接下来,我们有 reducer 和初始状态。

在计数器组件的上下文中,withReducer() 将更新计数的状态:计数将随着我们称之为“增加”的操作而增加,反之,计数将随着我们称之为“减少”的操作而减少。

首先,我们通过组合 withReducerwithHandlers 来创建一个增强组件。

const enhanced = compose(
  withReducer(
    "count",
    "dispatch",
    (state, action) => {
      switch (action.type) {
        case "INCREMENT":
          return state + 1;
        case "DECREMENT":
          return state - 1;
        default:
          return state;
      }
    },
    0
  ),
  withHandlers({
    incrementCounter: ({ dispatch }) => e => dispatch({ type: "INCREMENT" }),
    decrementCounter: ({ dispatch }) => e => dispatch({ type: "DECREMENT" })
  })
);

incrementCounterdecrementCounter 将响应 DOM 事件并分派操作类型。 状态将根据操作类型进行更新。 接下来,我们需要在组件中使用它。

const App = enhanced(({ count, incrementCounter, decrementCounter }) => {
  return (
    <div>
      <p>{count}</p>
      <button onClick={incrementCounter}>Increment</button>
      <button onClick={decrementCounter}>Decrement</button>
    </div>
  );
});

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的 Recompose – reducer

非常棒,对吧?

希望这能让你对 Recompose 是什么以及库中的丰富助手如何简化 React 开发有一个很好的了解,特别是在管理和调用状态方面。

当然,Recompose 比我们这里介绍的内容要多得多。 快速浏览一下 API 文档 将显示,您根据应用程序的要求可以使用许多高阶组件。 开始构建吧,如果您有任何问题,请随时在下方留言!