目录
React和TypeScript是构建更安全,更具伸缩性的Web应用程序的强大组合。
不幸的是,并非JavaScript社区采用的每种模式都很好地转换为TypeScript。例如,通过围绕新的动态创建的组件包含通用逻辑,实现更高阶的组件,从而实现代码重用。“随时随地制作”是JavaScript哲学中受尊重的(如果不是预期的)原则,但动态创建的接口在编译时通常很难描述(如果不是不可能的话)。
输入钩子,一种替代的TypeScript友好模式,用于代码重用。他们已准备好插入您的其他类型安全的React应用程序,他们将尊重代码中的现有约束。只需更新react,react-dom> = 16.8.0:
$ npm i --save \
[email protected] \
[email protected]
我们也需要更新的typings。钩子定义latest出现在v16.7.0之后:
$ npm i --save \
@types/[email protected] \
@types/[email protected]
使用类型定义和工作的TypeScript配置,我们已准备好开始使用钩子。
示例:typed useReducer ()
让我们使用React的useReducer钩子重写经典的计数器示例。该reducer问题是完全一样的(由执行,签名和类型定义),你可能会在使用类型安全的终极版的应用程序。
// reducer.ts
type Action = 'INCREMENT' | 'DECREMENT';
type State = {
count: number;
};
const reducer = (state: State, action: Action) => {
switch (action) {
case 'INCREMENT':
return {count: state.count + 1};
case 'DECREMENT':
if (state.count === 0) {
return state;
}
return {count: state.count - 1};
default:
return state;
}
};
export default reducer;
然而,React的useReducer()钩子现在接管了我们的状态容器,而不是中央的redux存储。
import * as React from 'react';
import reducer from './reducer';
const Counter: React.SFC<{}> = () => {
const [state, dispatch] = React.useReducer(reducer, {count: 0});
return (
<div>
<h1>Count: {state.count}</h1>
<div>
<button onClick={() => dispatch('INCREMENT')}>+1</button>
<button onClick={() => dispatch('DECREMENT')}>-1</button>
</div>
</div>
);
};
最好的部分?useReducer开箱即用的类型将推断出我们reducer的形状,并正确地提出明显的错误。尝试更改count为string-而不使用显式类型注释,TypeScript将正确地推断出state.count需要是 number。
这太容易了吗?
因此,钩子可以很好地(非常好地)与静态类型应用程序的其余部分一起使用。不过,有几点需要注意。
- 编译器只知道它的内容。Hook 接口 是键入的,但它们的类型是简单的,通用的,并且是最低限度的规范。自定义钩子实现中发生了什么?那是我们的事。
- Hooks期望在React组件中运行。这在生产中是一个非问题,其中一切都发生在组件内部,但它确实使测试稍微复杂化。
测试自定义钩子
实际上,让我们尝试测试一个自定义钩子。
const useCapitalized =
(s: string): ReturnType<typeof React.useState> => {
const [state, setState] = React.useState(s);
return [state.toUpperCase(), setState];
};
我们不会在实践中使用useCapitalized——toUpperCase()工作正常,谢谢你,不需要额外的样板——但它现在是一个有用的MacGuffin。
为了测试它,我们就希望能够写类似以下内容:
test('sets initial state', () => {
const [actual] = useCapitalized('Hello, world');
expect(actual).toEqual('HELLO, WORLD');
}
但是这个测试本身还不够。为了使它通过测试,我们需要一个使钩子可以在里面运行的最小测试组件。添加它,我们的最终测试看起来像这样:
import * as React from 'react';
import * as TestRenderer from 'react-test-renderer';
// ...
const HelloWorld = () => {
const [state] = useCapitalized('Hello, world');
return <b>{state}</b>;
};
test('.useCapitalized', () => {
const testRenderer = TestRenderer.create(<HelloWorld />);
expect(testRenderer.toJSON()).toMatchSnapshot();
});
一点也不差,在测试工具复杂度的大方案中,判断结果如下:在易于集成和易于测试之间,React的钩子应该是TypeScript开发人员多年来的好朋友。
原文地址:https://www.codeproject.com/Articles/1277165/Using-React-Hooks-with-TypeScript