类型定义速查表
React.FC<Props> | React.FunctionComponent<Props>
- 表示功能组件的类型
const MyComponent:react.FC <props> = ...
React.Component<Props, State>
- 表示类组件的类型
class MyComponent extends React.Component<Props, State> { ...
React.ComponentType<Props>
- 表示 (React.FC | React.Component) 联合的类型 - 在 HOC 中使用
const withState = <P extends WrappedComponentProps>(
WrappedComponent: React.ComponentType<P>,
) => { ...
React.ComponentProps<typeof XXX>
- 获取指定组件 XXX 的道具类型(警告:不适用于静态声明的默认道具和通用道具)
type MyComponentProps = React.ComponentProps<typeof MyComponent>;
React.ReactElement | JSX.Element
- 表示 React Element 概念的类型 - 表示原生 DOM 组件(例如
),或用户定义的复合组件(例如)
const elementOnly: React.ReactElement = <div /> || <MyComponent />;
React.ReactNode
- 代表任何可能类型的 React 节点的类型(基本上是 ReactElement(包括 Fragment 和 Portal)+原始 JS 类型)
const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
const Component = ({ children: React.ReactNode }) => ...
React.CSSProperties
- 在 JSX 中表示样式对象的类型 - 用于 css-in-js 样式
const styles: React.CSSProperties = { flexDirection: 'row', ...
const element = <div style={styles} ...
React.HTMLProps<HTMLXXXElement>
- 表示指定 HTML 元素的道具的类型 - 用于扩展 HTML 元素
const Input: React.FC<Props & React.HTMLProps<HTMLInputElement>> = props => { ... }
<Input about={...} accept={...} alt={...} ... />
React.ReactEventHandler<HTMLXXXElement>
- 表示通用事件处理程序的类型 - 用于声明事件处理程序
const handleChange:react.ReactEventHandler < HTMLInputElement > = ( ev ) => { ... }
< input onChange = { handleChange } ... / >
React.XXXEvent<HTMLXXXElement>
- 代表更具体事件的类型。一些常见的事件示例:ChangeEvent, FormEvent, FocusEvent, KeyboardEvent, MouseEvent, DragEvent, PointerEvent, WheelEvent, TouchEvent.
const handleChange = ( ev : React . MouseEvent < HTMLDivElement > ) => { ... }
< div onMouseMove = { handleChange } ... / >
redux Toolkit使用方法
- 目录结构
- store/index
// redux store 常规配置方法
// import { applyMiddleware, createStore } from 'redux'
// import { composeWithDevTools } from 'redux-devtools-extension'
// import thunkMiddleware from 'redux-thunk'
// import monitorReducersEnhancer from './enhancers/monitorReducers'
// import loggerMiddleware from './middleware/logger'
// import rootReducer from './reducers'
// export default function configureStore(preloadedState) {
// const middlewares = [loggerMiddleware, thunkMiddleware]
// const middlewareEnhancer = applyMiddleware(...middlewares)
// const enhancers = [middlewareEnhancer, monitorReducersEnhancer]
// const composedEnhancers = composeWithDevTools(...enhancers)
// const store = createStore(rootReducer, preloadedState, composedEnhancers)
// if (process.env.NODE_ENV !== 'production' && module.hot) {
// module.hot.accept('./reducers', () => store.replaceReducer(rootReducer))
// }
// return store
// }
// import { createStore, applyMiddleware, compose } from 'redux'
// import thunk from 'redux-thunk'
// import * as reducer from './reducer'
// const composeEnhancers =
// (typeof window !== 'undefined' &&
// window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
// compose
// export default createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
import { configureStore, MiddlewareArray } from '@reduxjs/toolkit'
import counterReducer from './features/counter/counterSlice'
// import logger from 'redux-logger'
// import additionalMiddleware from 'additional-middleware'
// import monitorReducersEnhancer from './enhancers/monitorReducers'
// // @ts-ignore
// import untypedMiddleware from 'untyped-middleware'
export const store: any = configureStore({
reducer: {
counter: counterReducer
}
// 中间件
// 如果你提供了 middleware 参数,则 configureStore 将仅使用你列出的 middleware。
// 如果你想同时拥有自定义的 以及 默认的 middleware,
// 你可以调用 getDefaultMiddleware 并将结果包括在你提供的 middleware 数组中。
// middleware: (getDefaultMiddleware) =>
// getDefaultMiddleware()
// .prepend(
// // correctly typed middlewares can just be used
// additionalMiddleware,
// // you can also type middlewares manually
// untypedMiddleware as Middleware<
// (action: Action<'specialAction'>) => number,
// RootState
// >
// )
// // prepend and concat calls can be chained
// .concat(logger),
// middleware: new MiddlewareArray().concat(additionalMiddleware, logger),
// 写法不同,与上述写法功能相同
// middleware: [additionalMiddleware, logger] as const
// preloadedState(初始化state)是如何控制单一或通过combineReducer组合生成的reducer中
// 手动加的state的默认值是否生效的
// preloadedState,
// enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。
// 这与 middleware 相似,它也允许你通过复合函数改变 store 接口。
// enhancers: [monitorReducersEnhancer]
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
- store/features/counter/couterSlice
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { fetchCount } from '../../../services/counterAPI'
const initialState = {
value: 0,
status: 'idle'
}
// The function below is called a thunk and allows us to perform async logic. It
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
// will call the thunk with the `dispatch` function as the first argument. Async
// code can then be executed and other actions can be dispatched. Thunks are
// typically used to make async requests.
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
async (amount) => {
const response = await fetchCount(amount)
// The value we return becomes the `fulfilled` action payload
return response.data
}
)
export const counterSlice = createSlice({
name: 'counter',
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
setStatus: (state) => {
if (state.loading === 'idle') {
state.loading = 'pending'
}
},
usersReceived (state, action) {
if (state.loading === 'pending') {
state.loading = 'idle'
state.value = action.payload
}
},
decrement: (state) => {
state.value -= 1
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action) => {
state.value += action.payload
}
},
// The `extraReducers` field lets the slice handle actions defined elsewhere,
// including actions generated by createAsyncThunk or in other slices.
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = 'loading'
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'idle'
state.value += action.payload
})
}
})
export const { increment, decrement, incrementByAmount, setStatus, usersReceived } = counterSlice.actions
// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
export const selectCount = (state) => state.counter.value
// We can also write thunks by hand, which may contain both sync and async logic.
// Here's an example of conditionally dispatching actions based on current state.
export const incrementIfOdd = (amount) => (dispatch, getState) => {
const currentValue = selectCount(getState())
if (currentValue % 2 === 1) {
dispatch(incrementByAmount(amount))
}
}
export default counterSlice.reducer
// 定义一个 thunk 派发那些 action creators 异步方法
// const fetchUsers = () => async dispatch => {
// dispatch(setStatus())
// const response = await fetchCount()
// dispatch(usersReceived(response.data))
// }
- services/couterAPI
// A mock function to mimic making an async request for data
export function fetchCount (amount = 1) {
return new Promise((resolve) =>
setTimeout(() => resolve({ data: amount }), 500)
)
}
- hooks/useRedux
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from '../store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
- 使用方法 pages/reudx
import React, { ReactElement } from 'react'
import { useAppSelector, useAppDispatch } from '@/hooks/useRedux'
import { decrement, increment, incrementByAmount } from '../../store/features/counter/counterSlice'
export default function Redux (): ReactElement {
const count = useAppSelector((state) => state.counter.value)
const dispatch = useAppDispatch()
const add = () => {
console.log(count)
// dispatch(increment())
// dispatch.counter.decrement()
dispatch(incrementByAmount(3))
}
return (
<div>
{count}
<button onClick={add}>按钮</button>
</div>
)
}
- 效果