天天看点

typescript在react中的使用配置详解1 环境2 function组件3 redux4 ts中使用js

1 环境

$ npx create-react-app test --typescript
           

2 function组件

props

一般类型

interface IProps{
    param_1: string
    param_2: number
    param_3: boolean
    param_4: string[]
    param_5?: number[]
    ......
}

function App(props:IProps) {
    return null
}
           

类型为function组件

import React from 'react';

interface ITest {  name: string }

function Test({ name }: ITest) {
  return <span>{name}</span>
}

function App({ TestComponent }: { TestComponent: React.FC<ITest> }) {
  return  <TestComponent name={'xxx'} />
}

export default function () {
  return <App
    TestComponent={Test}
  />
}
           

类型为class组件

import React from 'react';

interface IProps {  age: number  }
interface IState {  name: string  }

class Test extends React.Component<IProps, IState> {

  state = { name: '林' }

  render() {
    return <span>{this.props.age}</span>
  }
}

function App({ TestComponent }: {  TestComponent: React.ComponentType<IProps> }) {
  return <TestComponent age={18} />
}

export default function () {
  return <App
    TestComponent={Test}
  />
}
           

useReducer

import React, { useReducer } from 'react';

type Action = {
  type: 'setOpen';
  data: {open: boolean}
} | {
  type: 'setName';
  data: {name: string}
}

interface IInit {
  open: boolean
  name: string
}

const init: IInit = {
  open: true,
  name: '1'
}

function myReducer(data: IInit, action: Action): IInit {
  switch (action.type) {
    case 'setOpen':
      return {...data,...action.data}
    case 'setName':
      return {...data,...action.data}
    default:
      return data;
  }
}

function App() {

  const [state, dispatch] = useReducer(myReducer, init);

  return (
    <>
      <div>{state.open ? 'open为true' : 'open为false'}</div>
      <button
        onClick={() => {
          if (state.open) {
            dispatch({ type: 'setOpen', data: { open: false } })
          } else {
            dispatch({ type: 'setOpen', data: { open: true } })
          }
        }}
      >
        切换open
      </button>
      <button
        onClick={() => dispatch({ type: 'setName', data: { name: '2' } })}
      >
        切换flag
      </button>
    </>
  );
}

export default App;
           

useRef

import React, { useRef, MutableRefObject } from 'react';
import Child from './Child';

function Father() {

  const h1Ref = useRef() as MutableRefObject<HTMLHeadingElement>;
  const divRef = useRef() as MutableRefObject<HTMLDivElement>;
  const ulRef = useRef() as MutableRefObject<HTMLUListElement>;

  return (
    <>
      <h1 ref={h1Ref}></h1>
      <div ref={divRef}></div>
      <ul ref={ulRef}></ul>
      <canvas></canvas>
    </>
  )
}

export default Father;
           

useImperativeHandle

import React, { useImperativeHandle } from 'react';

export interface IChildHandler {
    speak: () => void
}

interface IChildProps { name?: string }

const Child = React.forwardRef<IChildHandler, IChildProps>(({ name }, ref) => {

    useImperativeHandle(ref, () => ({
        speak() {
            console.log('访问到组件里的内容了')
        }
    }))

    return null;
});

export default Child;
           

useRef | useImperativeHandle

import React, { useRef, MutableRefObject, useCallback } from 'react';
import Child, { IChildHandler } from './Child';

function Father() {

  const childRef = useRef() as MutableRefObject<IChildHandler>;

  return <>
    <Child ref={childRef} />
    <button onClick={
      useCallback(() => {
        childRef.current.speak();
      }, [])
    }>
      触发child中的speak
    </button>
  </>
}

export default Father;
           

useContext

  • context.ts
import { createContext } from 'react';

export interface IContext {
  name: string 
  obj: {
    age: number
  }
}

export default createContext<Partial<IContext>>({});
           
  • Father.tsx
import MyContext from './context';

function Father() {
  return (
    <MyContext.Provider value={{ name: '林', obj: { age: 18 } }}>
      <Child />
    </MyContext.Provider >
  )
}
           
  • Child.tsx
import MyContext, { IContext } from './context';

function Child() {

  const { name, obj } = useContext(MyContext) as IContext;

  return <span>{name}-{obj.age}</span>
}
           

3 redux

官方例子 https://redux.js.org/recipes/usage-with-typescript

$ yarn add react-redux redux  @types/react-redux
           

3.0 基础文件

3.0.1 store/login/type.ts

// src/store/login/type.ts

import { SET_LOGIN } from './';

export type LoginState = boolean;   // 定义登陆状态的数据类型

export interface UpdateLogin {    // 定义登陆状态的action
  type: typeof SET_LOGIN
  payload: LoginState
}
           

3.0.2 store/login/index.ts

// src/store/login/index.ts

import { LoginState, UpdateLogin } from './type';

// Key
export const SET_LOGIN = 'SET_LOGIN';

// initalValue
const initialState: LoginState = false;   // 是否已经登陆

// Action
export function updateLoginState(newLoginState: LoginState): UpdateLogin {
  return {
    type: SET_LOGIN,
    payload: newLoginState
  }
}

// Reducer
export default function (state = initialState, action: UpdateLogin): LoginState {
  switch (action.type) {
    case SET_LOGIN: return action.payload;
    default: return state;
  }
}
           

3.0.3 store/chat/type.ts

// src/store/chat/type.ts

import { SEND_MESSAGE, DELETE_MESSAGE } from './';

export interface Message {
  user: string
  message: string
  timestamp: number
}

export interface ChatState {
  messages: Message[]
}

interface SendMessageAction {
  type: typeof SEND_MESSAGE
  newMessage: Message
}

interface DeleteMessageAction {
  type: typeof DELETE_MESSAGE
  timestamp: number
}
export type ChatActionTypes = SendMessageAction | DeleteMessageAction
           

3.0.4 store/chat/index.ts

// src/store/chat/index.ts

import { ChatActionTypes, ChatState, Message } from './type';

// Key
export const SEND_MESSAGE = 'SEND_MESSAGE'
export const DELETE_MESSAGE = 'DELETE_MESSAGE'

// initalValue
const initialState: ChatState = { messages: [] }

// Action
export function sendMessage(newMessage: Message): ChatActionTypes {
  return { type: SEND_MESSAGE, newMessage }
}

export function deleteMessage(timestamp: number): ChatActionTypes {
  return { type: DELETE_MESSAGE, timestamp }
}

// Reducer
export default function (
  state = initialState,
  action: ChatActionTypes
): ChatState {
  switch (action.type) {
    case SEND_MESSAGE:
      return { messages: [...state.messages, action.newMessage] }
    case DELETE_MESSAGE:
      return {
        messages: state.messages.filter(
          message => message.timestamp !== action.timestamp
        )
      }
    default:
      return state
  }
}
           

3.0.5 store/index.ts

// src/store/index.ts

import { combineReducers, createStore } from 'redux';
import loginRefucer from './login'
import chatReducer from './chat'

const rootReducer = combineReducers({
  login: loginRefucer,
  chat: chatReducer
})

export type RootState = ReturnType<typeof rootReducer>

export default createStore(rootReducer);
           

3.0.6 index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store/index';
import Child from './child'

function Father() {
  return (
    <Provider store={store}>
      <Child />
    </Provider>
  )
}

ReactDOM.render(<Father />, document.getElementById('root'));
           

3.1 hooks

3.1.1 useSelector

import React from 'react';
import { Provider, useSelector, useStore, useDispatch } from 'react-redux';
import store, { RootState } from './store/index'

function Child() {

  const loginState = useSelector((state: RootState) => state.login);

  return <span>现在的登陆状态为:{loginState ? '已登陆' : '未登陆'}</span>
}
           

3.1.2 useDispatch

  • store/index.ts
  • index.tsx
....
import store, { RootState, AppDispatch } from './store/index'


function Child(){
    
    const dispatch: AppDispatch = useDispatch();
    
    useEffect(()=>{
        dispatch({ type: 'SET_LOGIN', payload: true });
	},[])
    
    return null
}
           

4 ts中使用js

4.1 导入js组件

如果在ts项目中导入js写的库,因为没有类型的描述文件就会报错

typescript在react中的使用配置详解1 环境2 function组件3 redux4 ts中使用js
typescript在react中的使用配置详解1 环境2 function组件3 redux4 ts中使用js

这是因为这个库过于老旧或者官方没有为其编写类型文件,解决这个方法之一,就是在 tsconfig.josn 中添加下面一行配置

{
	......
    "noImplicitAny": false,
    ......
}
           

现在再编译就不会报错了,虽然不会报错,但是引入的库后面会显示这几个小点点,表示ts的编译器不知道其类型,相当于any

typescript在react中的使用配置详解1 环境2 function组件3 redux4 ts中使用js

4.2 安装别人写好的声明文件

先把

noImplicitAny

改为

true

, 还记得上面的提示吗,

webpack

让 我们 安装

@types/react-fastclick

,这是因为 ts 为了让一些用js写成的库在ts环境下能正常访问,所以如果 这个库的声明文件在 源码目录下找不到,会去

node_modules

@types

目录下查找相关的声明文件

这个目录下一般是官方不想使用 ts 重写一遍代码,所以直接的写一遍类型文件

或者为这个库的爱好者为其官方书写的类型文件

typescript在react中的使用配置详解1 环境2 function组件3 redux4 ts中使用js

4.2 自定义d.ts

但是我们通过安装发现,并没有

@types/react-fastclick

这个库,说明没有人为其书写类型声明文件,那我们可以自己的为其书写声明文件

  • 项目/tsconfig.json
{
  "compilerOptions": {
	......
    "baseUrl": ".",
    "paths": {
      "*":[
        "./node_modules/@tpyes",   // 声明文件先去node_modules/@tpyes下找
        "./typings/*"    // 如果node_modules下找不到,就去这个文件夹下找
      ]
    }
  },
}
           
  • 项目/typings/react-fastclick/index.d.ts
declare function fastClick(): void;

export default fastClick;
           

编写完成之后,就会发现 vscode 不会有三个小点了

typescript在react中的使用配置详解1 环境2 function组件3 redux4 ts中使用js