(this.rootDomRef = ref)} />;
}
}
```
用 React Hooks 可以这么做:
```tsx
function App() {
const ref = React.useRef(null);
let chart: G2.Chart = null;
React.useEffect(() => {
chart = new G2.Chart({
container: ReactDOM.findDOMNode(ref.current) as HTMLDivElement,
width: 500,
height: 500
});
// do something
chart.render();
return () => chart.destroy();
}, []);
return
;
}
```
可以看到将细碎的代码片段结合成了一个完整的代码块,更易维护。
现在介绍了 `useState` `useContext` `useEffect` `useRef` 等常用 hooks,更多可以查阅:[内置 Hooks](https://reactjs.org/docs/hooks-reference.html),相信不久的未来,这些 API 又会成为一套新的前端规范。
## 3 精读
### Hooks 带来的约定
Hook 函数必须以 "use" 命名开头,因为这样才方便 eslint 做检查,防止用 condition 判断包裹 useHook 语句。
为什么不能用 condition 包裹 useHook 语句,详情可以见 [官方文档](https://reactjs.org/docs/hooks-rules.html#explanation),这里简单介绍一下。
React Hooks 并不是通过 Proxy 或者 getters 实现的(具体可以看这篇文章 [React hooks: not magic, just arrays](https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e)),而是通过链表实现的,每次 `useState` 都会改变下标,如果 `useState` 被包裹在 condition 中,那每次执行的下标就可能对不上,导致 `useState` 导出的 `setter` 更新错数据。
虽然有 [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) 插件保驾护航,但这第一次将 “约定优先” 理念引入了 React 框架中,带来了前所未有的**代码命名和顺序限制**(函数命名遭到官方限制,JS 自由主义者也许会暴跳如雷),但带来的便利也是前所未有的(没有比 React Hooks 更好的状态共享方案了,约定带来提效,自由的代价就是回到 renderProps or HOC,各团队可以自行评估)。
笔者认为,React Hooks 的诞生,也许来自于这个灵感:“不如通过增加一些约定,彻底解决状态共享问题吧!”
> React 约定大于配置脚手架 [nextjs](https://github.com/zeit/next.js) [umi](https://github.com/umijs/umi) 以及笔者的 [pri](https://github.com/prijs/pri) 都通过有 “约定路由” 的功能,大大降低了路由配置复杂度,**那么 React Hooks 就像代码级别的约定**,大大降低了代码复杂度。
### 状态与 UI 的界限会越来越清晰
因为 React Hooks 的特性,如果一个 Hook 不产生 UI,那么它可以永远被其他 Hook 封装,虽然允许有副作用,但是被包裹在 `useEffect` 里,总体来说还是挺函数式的。而 Hooks 要集中在 UI 函数顶部写,也很容易养成书写无状态 UI 组件的好习惯,践行 “状态与 UI 分开” 这个理念会更容易。
不过这个理念稍微有点蹩脚的地方,那就是 “状态” 到底是什么。
```tsx
function App() {
const [count, setCount] = useCount();
return
{count};
}
```
我们知道 `useCount` 算是无状态的,因为 React Hooks 本质就是 renderProps 或者 HOC 的另一种写法,换成 renderProps 就好理解了:
```tsx
{(count, setCount) => };
function App(props) {
return
{props.count};
}
```
可以看到 App 组件是无状态的,输出完全由输入(Props)决定。
那么有状态无 UI 的组件就是 `useCount` 了:
```tsx
function useCount() {
const [count, setCount] = useState(0);
return [count, setCount];
}
```
有状态的地方应该指 `useState(0)` 这句,不过这句和无状态 UI 组件 App 的 `useCount()` 很像,既然 React 把 `useCount` 成为自定义 Hook,那么 `useState` 就是官方 Hook,具有一样的定义,因此可以认为 `useCount` 是无状态的,`useState` 也是一层 renderProps,最终的状态其实是 `useState` 这个 React 内置的组件。
我们看 renderProps 嵌套的表达:
```tsx
{(count, setCount) => (
{" "}
{/**虽然是透传,但给 count 做了去重,不可谓没有作用 */}
{(count, setCount) => }
)}
```
能确定的是,App 一定有 UI,而上面两层父级组件一定没有 UI。为了最佳实践,我们尽量避免 App 自己维护状态,而其父级的 RenderProps 组件可以维护状态(也可以不维护状态,做个二传手)。因此可以考虑在 “有状态的组件没有渲染,有渲染的组件没有状态” 这句话后面加一句:没渲染的组件也可以没状态。
## 4 总结
把 React Hooks 当作更便捷的 RenderProps 去用吧,虽然写法看上去是内部维护了一个状态,但其实等价于注入、Connect、HOC、或者 renderProps,那么如此一来,使用 renderProps 的门槛会大大降低,因为 Hooks 用起来实在是太方便了,我们可以抽象大量 Custom Hooks,让代码更加 FP,同时也不会增加嵌套层级。
## 5 更多讨论
> 讨论地址是:[精读《React Hooks》 · Issue #111 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/111)
**如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**