将Typescript与React结合使用-入门指南

朋友,在周末的前夕,我们想与您分享另一本有趣的出版物,我们希望与“ JavaScript开发人员”课程的新小组启动同时进行。



在过去的几个月中,我使用Typescript开发了React应用程序和库,因此我决定分享我在这段时间内学到的一些东西。 在本指南中,我将向您介绍80%的情况下用于Typescript和React的模板。

我应该学习用于React应用程序开发的打字稿吗? 值得,仍然值得! 就我自己而言,我在实践中意识到严格的打字会导致编写更可靠的代码,加快开发速度,尤其是在大型项目中。 刚开始时,您可能会感到失望,但是在工作时,您会发现至少一个最小的模板确实会很有帮助。

而且,如果您卡在某个东西上,请记住,您始终可以输入任何类似的东西。 任何人都是您的新朋友。 现在,我们将直接介绍示例。

您的核心反应组件与打字稿


标准反应组件在打字稿上看起来像什么? 让我们将其与javascript中的react组件进行比较。

import React from 'react' import PropTypes from 'prop-types' export function StandardComponent({ children, title = 'Dr.' }) { return ( <div> {title}: {children} </div> ) } StandardComponent.propTypes = { title: PropTypes.string, children: PropTypes.node.isRequired, } 

现在是打字稿版本:

 import * as React from 'react' export interface StandardComponentProps { title?: string children: React.ReactNode } export function StandardComponent({ children, title = 'Dr.', }: StandardComponentProps) { return ( <div> {title}: {children} </div> ) } 

非常相似,对不对? 我们用propTypes接口替换了propTypes

prop标头仍然是可选的,而继承人的prop仍然是必需的。 我们导出了接口,以防其他组件​​需要链接。

扩展标准HTML属性


如果我们希望父组件能够提供其他类型化的div属性,例如aria-hiddenstyleclassName ,则可以在interface定义它们或扩展内置接口。 在下面的示例中,我们说我们的组件除了标头和后代之外,还接受任何标准div属性。

 import * as React from 'react' export interface SpreadingExampleProps extends React.HTMLAttributes<HTMLDivElement> { title?: string children: React.ReactNode } export function SpreadingExample({ children, title = 'Dr.', ...other }: SpreadingExampleProps) { return ( <div {...other}> {title}: {children} </div> ) } 

事件处理


我们可以对事件处理程序进行类型化,以确保事件参数的类型正确。 下面的示例演示了实现此目标的各种方法:

 export interface EventHandlerProps { onClick: (e: React.MouseEvent) => void } export function EventHandler({ onClick }: EventHandlerProps) { // handle focus events in a separate function function onFocus(e: React.FocusEvent) { console.log('Focused!', e.currentTarget) } return ( <button onClick={onClick} onFocus={onFocus} onKeyDown={e => { // When using an inline function, the appropriate argument signature // is provided for us }} > Click me! </button> ) } 

不确定要使用哪个参数签名? 在编辑器中,将鼠标悬停在事件处理程序的相应属性上。

将泛型与React组件一起使用


这是一项更高级的功能,但确实功能强大。 通常,您在具有特定属性的React组件中定义数据类型。 假设您的组件需要一个profile对象。

 interface ProfileType { name: string image: string age: number | null } interface ProfilesProps { profiles: Array<ProfileType> } function Profiles(props: ProfilesProps) { // render a set of profiles } 

现在,让我们假设您有一个可以接受任何类型数组的组件。 泛型就像邮寄包裹。 快递员(我们的组件)不需要知道您要发送的包裹的内容,但是发件人(父组件)希望收件人收到他发送的内容。

我们这样实现:

 interface GenericsExampleProps<T> { children: (item: T) => React.ReactNode items: Array<T> } export function GenericsExample<T>({ items, children, }: GenericsExampleProps<T>) { return ( <div> {items.map(item => { return children(item) })} </div> ) } 

有点奇怪的例子……尽管如此,它还是展示了本质。 该组件接受任何类型的元素数组,通过它,然后将children作为具有数组元素的渲染函数调用。 当我们的父组件将render renderer作为继承者提供时,该元素将被正确键入!

不明白吗? 这很正常。 我自己尚未弄清楚泛型,但您不太可能需要全面了解它们。 但是,您使用typescript的次数越多,它就越有意义。

打字钩


挂钩通常开箱即用。 这两个例外只能是useRefuseReducer 。 下面的示例显示了如何键入引用。

 import * as React from 'react' interface HooksExampleProps {} export function HooksExample(props: HooksExampleProps) { const [count, setCount] = React.useState(0) const ref = React.useRef<HTMLDivElement | null>(null) // start our timer React.useEffect( () => { const timer = setInterval(() => { setCount(count + 1) }, 1000) return () => clearTimeout(timer) }, [count] ) // measure our element React.useEffect( () => { if (ref.current) { console.log(ref.current.getBoundingClientRect()) } }, [ref] ) return <div ref={ref}>Count: {count}</div> } 

我们的状态是自动键入的,但是我们手动键入ref表示它将为null或包含div元素。 当我们在useEffect函数中访问ref时,需要确保它不为null

变速箱打字


变速箱要稍微复杂一点,但是如果正确输入,那就太好了。

 // Yeah, I don't understand this either. But it gives us nice typing // for our reducer actions. type Action<K, V = void> = V extends void ? { type: K } : { type: K } & V // our search response type interface Response { id: number title: string } // reducer actions. These are what you'll "dispatch" export type ActionType = | Action<'QUERY', { value: string }> | Action<'SEARCH', { value: Array<Response> }> // the form that our reducer state takes interface StateType { searchResponse: Array<Response> query: string } // our default state const initialState: StateType = { searchResponse: [], query: '', } // the actual reducer function reducer(state: StateType, action: ActionType) { switch (action.type) { case 'QUERY': return { ...state, query: action.value, } case 'SEARCH': return { ...state, searchResponse: action.value, } } } interface ReducerExampleProps { query: string } export function ReducerExample({ query }: ReducerExampleProps) { const [state, dispatch] = React.useReducer(reducer, initialState) React.useEffect( () => { if (query) { // emulate async query setTimeout(() => { dispatch({ type: 'SEARCH', value: [{ id: 1, title: 'Hello world' }], }) }, 1000) } }, [query] ) return state.searchResponse.map(response => ( <div key={response.id}>{response.title}</div> )) } 

使用typeofkeyof代表组件选项


假设我们需要一个可以具有不同外观的按钮,每个按钮都在具有一组键和样式的对象中定义,例如:

 const styles = { primary: { color: 'blue', }, danger: { color: 'red', }, } 

我们的按钮组件应接受type属性,可以是
styles对象中的任何键(例如“ primary”“ danger” )。 我们可以很简单地输入:

 const styles = { primary: { color: 'blue', }, danger: { color: 'red', }, } // creates a reusable type from the styles object type StylesType = typeof styles // ButtonType = any key in styles export type ButtonType = keyof StylesType interface ButtonProps { type: ButtonType } export function Button({ type = 'primary' }: ButtonProps) { return <button style={styles[type]}>My styled button</button> } 

这些示例将帮助您完成80%的操作。 如果您被卡住,那通常值得
只需看看现有的开源示例。

Sancho UI是一组反应组件,
使用打字稿和情感来构建。
蓝图是另一组组件
react建立在typescript

好吧,根据既定传统,我们正在等待您的评论。

Source: https://habr.com/ru/post/zh-CN456124/


All Articles