前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从一个PR窥探React未来开发方式

从一个PR窥探React未来开发方式

作者头像
公众号@魔术师卡颂
发布2021-08-26 16:22:10
4190
发布2021-08-26 16:22:10
举报
文章被收录于专栏:魔术师卡颂魔术师卡颂

大家好,我是卡颂。

都说HooksReact的未来,但Hooks的最佳实践是什么呢?

关于这块知识,官方文档一点儿都没提及。

所以在实际项目中,常会出现类似下面的问题:

代码语言:javascript
复制
// ...
useEffect(() => {
  fetchData(a, b).then(
    // ...
  )
}, [a, b])
//...

useEffect依赖了a b两个状态,当其中任意一个变化后会执行fetchData请求数据。

当应用变得复杂,要追踪ab何时变化变得越来越难。

假以时日接口调整,fetchData还需要状态c作为参数。那么追踪状态变化的难度又会进一步提高。

最终会导致:

  • 轻则无意义的fetchData多次调用
  • 重则逻辑出现难以追查的bug

有朋友会说:你可以封装自定义Hook啊。

这只是将问题隐藏的更深了......

如何解决这个问题

以上问题的本质原因是:「副作用」实在太多,可以被当作「副作用」的东西也实在太多。这导致useEffect被滥用。

所以,要解决滥用问题,就需要为不同类型「副作用」提供官方解决方案。

这样,具体问题有了具体解决方案,才不会useEffect一把梭。

从一个PR看到变化

最近React有个很不起眼的PR[1]:

大体意思是:

在之前,当你在一个已经卸载的组件(unmounted)中调用setState会触发一个warning,这个PR将移除这个warning

举个例子,以下代码在组件mount时注册handleChange

代码语言:javascript
复制
useEffect(() => {
  function handleChange() {
     setState(store.getState())
  }
  store.subscribe(handleChange)
  
  return () => store.unsubscribe(handleChange)
}, [])

如果你忘记写这行解绑代码:

代码语言:javascript
复制
return () => store.unsubscribe(handleChange)

那么组件卸载后handleChange也可能被调用,进而调用setState

这是潜在的内存泄漏。

在之前的React中,这种行为会报warning

那为什么要移除这种行为下的warning呢?

PR的背后

一方面,这个warning有一定概率误判,比如「点击按钮提交表单」

代码语言:javascript
复制
async function handleSubmit() {
  setPending(true)
  await post('/someapi')
  setPending(false)
}

点击按钮后调用setPending触发loading图标显示,接着发起post请求。

有可能请求返回前组件就卸载了,此时会调用:

代码语言:javascript
复制
setPending(false)

并不会有内存泄漏风险,但是会报warning

不过warning移除还有另一个更本质的原因:

在第一个示例中,我们在useEffect中调用store.subscribe,这种行为可以归类为:

在组件中订阅外部源

什么是「外部源」呢?

任何「变化与否不受React控制的源」都是「外部源」

比如:

  • 各种第三方状态管理库
  • 希望location.hash变化触发组件更新

未来所有这类行为都会收敛到useMutableSource这个Hook中。

更多例子

再比如,对于I/O操作(比如「请求数据」)这种大家都会放在useEffect中的逻辑,未来使用resource结合Suspense可能是更好的选择:

代码语言:javascript
复制
const resource = fetchDetail();

function Page() {
  return (
    <Suspense fallback={<h1>Loading...</h1>}>
      <Details/>
    </Suspense>
  );
}

function Details() {
  const data = resource.read();
  return <h1>{data.name}</h1>;
}

以上例子中,调用fetchDetail会发起数据请求。

Details组件调用resource.read直接消费数据即可。

如果数据还未返回,视图会渲染最近的Suspensefallback(即<h1>Loading...</h1>)。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 魔术师卡颂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何解决这个问题
  • 从一个PR看到变化
  • PR的背后
  • 更多例子
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档