React Hooks渲染道具将发生什么

47 阅读5分钟

大约一年前,我发表了《如何用道具获取器给用户提供渲染控制》。在那篇文章中,我展示了整个实现(在当时)的 react-toggled的整个实现,实际上,我建立这个实现的唯一目的是为了传授我在...中使用的一些模式。 downshift.它是一个更小、更简单的组件,实现了许多与downshift相同的模式,所以它是教授prop getters模式的一个好方法。

react-toggled和downshift都使用渲染道具模式作为React组件逻辑代码共享的机制。正如我在博文"何时不使用渲染道具 "中解释的那样,这是渲染道具模式的主要用例。但这也是React Hooks的主要使用情况。而且React Hooks比类组件+渲染道具要简单得多。

那么,这是否意味着当React Hooks稳定后,你就不再需要渲染道具了?不!不!不!不我可以想到两个场景,在这两个场景中,渲染道具模式仍然非常有用,我一会儿就会和你分享这些。但是,让我们通过比较当前版本的react-toggled 和基于钩子的实现,来确定我所说的钩子更简单的说法。

如果你有兴趣,这里是目前的源码,用于react-toggled.

下面是react-toggled 的一个典型用法:

function App() {
  return (
    
      {({on, toggle}) => {on ? 'on' : 'off'}}
    
  )
}

如果我们想要的只是简单的切换功能,我们的钩子版本将是:

function useToggle(initialOn = false) {
  const [on, setOn] = useState(initialOn)
  const toggle = () => setOn(!on)
  return {on, toggle}
}

然后人们可以像这样使用它:

function App() {
  const {on, toggle} = useToggle()
  return {on ? 'on' : 'off'}
}

酷!简单多了!但是 react-toggled 中的 Toggle 组件实际上支持更多。首先,它提供了一个名为getTogglerProps 的辅助工具,它将给你提供切换器工作所需的正确道具(包括aria属性,以实现可访问性)。所以,让我们来做这个工作:

// returns a function which calls all the given functions
const callAll =
  (...fns) =>
  (...args) =>
    fns.forEach(fn => fn && fn(...args))

function useToggle(initialOn = false) {
  const [on, setOn] = useState(initialOn)
  const toggle = () => setOn(!on)
  const getTogglerProps = (props = {}) => ({
    'aria-expanded': on,
    tabIndex: 0,
    ...props,
    onClick: callAll(props.onClick, toggle),
  })
  return {
    on,
    toggle,
    getTogglerProps,
  }
}

现在我们的useToggle 钩子可以使用getTogglerProps

function App() {
  const {on, getTogglerProps} = useToggle()
  return {on ? 'on' : 'off'}
}

而且它更容易被访问和使用。很好吧?那么,如果我的用例不需要getTogglerProps 呢?让我们把它分开一点。

// returns a function which calls all the given functions
const callAll =
  (...fns) =>
  (...args) =>
    fns.forEach(fn => fn && fn(...args))

function useToggle(initialOn = false) {
  const [on, setOn] = useState(initialOn)
  const toggle = () => setOn(!on)
  return {on, toggle}
}

function useToggleWithPropGetter(initialOn) {
  const {on, toggle} = useToggle(initialOn)
  const getTogglerProps = (props = {}) => ({
    'aria-expanded': on,
    tabIndex: 0,
    ...props,
    onClick: callAll(props.onClick, toggle),
  })
  return {on, toggle, getTogglerProps}
}

我们可以做同样的事情来支持getInputTogglerPropsgetElementTogglerProps 帮助器,目前支持react-toggled 。这实际上将允许我们轻松地树状摇出那些我们的应用程序不使用的额外的实用程序,这在渲染道具解决方案中是非常不符合人体工程学的(不是不可能,只是有点难看)。

**但是Kent!**我不想去重构我的应用程序中所有使用渲染道具API的地方,以使用新的钩子API!!

不要担心!看看这个吧。

const Toggle = ({children, ...props}) => children(useToggle(props))

这是你的渲染道具组件。你可以像使用旧的那样使用它,并随着时间的推移进行迁移。事实上,这就是我推荐的测试自定义钩子的方法

还有更多的内容(例如,我们如何将控制道具模式移植到反应钩子上)。我打算把这些留给你去发现。一旦你试了一下,再看我做。我们一直以来的测试方式有一个缺陷,那就是用钩子的方式会有一点变化(多亏了JavaScript闭包)。

好的,所以我们可以重构我们的组件来使用钩子,甚至可以继续用基于渲染道具的API来导出react组件(你可能会感兴趣,你甚至可以考虑用九头蛇模式来全部完成)。但让我们想象一下,我们现在处在一个不需要渲染道具来实现逻辑重用的未来,每个人都在使用钩子。还有什么理由继续编写或使用暴露了渲染道具API的组件吗?

是的!观察一下!这里有一个使用downshift和react-virtualized的例子。这里是相关的部分。

 (
    

看看那里的rowRenderer 道具。你知道那是什么吗?它是一个渲染道具!什么!?🙀这就是渲染道具的反转控制的全部魅力所在。那是一个道具,react-virtualized ,用来把对列表中的行的渲染控制权委托给你这个组件的用户。如果react-virtualized 被重写成使用钩子,也许它可以接受rowRenderer 作为useVirtualized 钩子的参数,但我真的看不出这比它目前的API有什么好处。所以,我认为渲染道具(以及这种控制反转的方式)在这样的使用情况下是可以保留的。

我希望你觉得这很有趣并且有帮助。请记住,React钩子仍处于alpha阶段,可能会有变化。它们也是完全可选择的,不需要对React的API做任何破坏性的改变。我认为这是件好事。不要去重写你的应用程序!重构它们(一旦钩子稳定了)!

祝您好运!