1 react
1.1 useState
让函数组件具有维持状态的能力
const[count, setCount]=useState(0);
1.2 useEffect
执行副作用,useEffect 的第二个参数告诉 React 用到了哪些外部变量
类似于Vue watch的作用
useEffect(fn, deps);
1.每次 render 后执行:不提供第二个依赖项参数。
比如useEffect(() => {})。
2.仅第一次 render 后执行:提供一个空数组作为依赖项。
比如useEffect(() => {}, [])。
3.第一次以及依赖项发生变化后执行:提供依赖项数组。
比如useEffect(() => {}, [deps])。
4.组件 unmount 后执行:返回一个回调函数。
比如useEffect() => { return () => {} }, [])。
useEffect(() => {
document.title = "Hello, " + name;
}, [name]);
1.3 useCallback
缓存回调函数。
useCallback(fn, deps)
useCallback是React Hooks中的一个函数,用于优化函数组件的性能。它的作用是返回一个memoized(记忆化的)函数,这个函数只有在依赖项发生变化时才会重新计算,否则会直接返回上一次计算的结果。
例子:
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(
() => setCount(count + 1),
[count], // 只有当 count 发生变化时,才会重新创建回调函数
);
return <button onClick={handleIncrement}>+</button>
}
例子2:
import { useState, useCallback } from 'react';
function MyComponent(props) {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log(`Clicked ${count} times`);
}, [count]);
return (
<>
<div>Count: {count}</div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={handleClick}>Click me</button>
</>
);
}
1.4 useMemo
缓存计算的结果,类似于Vue中computed的作用
useMemo(fn, deps);
1.5 useRef
在多次渲染之间共享数据
const myRefContainer =useRef(initialValue);
1.6 useContext
定义全局状态
很多状态管理框架,比如 Redux,正是利用了 Context 的机制来提供一种更加可控的组件之间的状态管理机制。
1.7 createRef
创建一个 React ref 对象
import React, { createRef } from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
// 创建一个 ref 对象
this.myRef = createRef();
}
render() {
return (
<div>
{/* 将 ref 对象附加到一个 DOM 元素上 */}
<input type="text" ref={this.myRef} />
</div>
);
}
}
1.8 memo
React.memo
是 React 提供的一个高阶组件(Higher-Order Component),用于优化函数组件的性能。它类似于类组件中的 shouldComponentUpdate
方法,用于在 props 发生变化时判断是否重新渲染组件。
import React from 'react';
const MyComponent = React.memo((props) => {
// 在这里定义组件逻辑
return (
<div>
{/* 使用 props 中的数据渲染组件 */}
<h1>{props.title}</h1>
<p>{props.content}</p>
</div>
);
});
1.9 组件创建
const ImgUpload: FC<IProps> = (props: IProps): ReactElement => {
return (<div></div>)
};
export default memo(ImgUpload);
1.10 Suspense
就是一种加载组件优化,提升用户体验。
children
:真正的 UI 渲染内容。
fallback
:真正的 UI 未渲染完成时代替其渲染的备用 UI,它可以是任何有效的 React 节点。
import { Suspense } from 'react';
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
2 Redux Toolkit
2.1 createSlice
接受初始状态的函数、reducer 函数的对象和“切片名称”, 并自动生成与 reducer 和 state 相对应的动作创建者和动作类型。类似于 Vue 中 Vuex 中的 store
function createSlice({
//此状态切片的字符串名称。生成的操作类型常量将使用此作为前缀。
name: string,
// 此状态切片的初始状态值。
initialState: State,
//一个包含 Redux “case reducer” 函数的对象
reducers: Record<string, ReducerFunction | ReducerAndPrepareObject>,
// A "builder callback" function used to add more reducers
extraReducers?: (builder: ActionReducerMapBuilder<State>) => void,
// A preference for the slice reducer's location, used by `combineSlices` and `slice.selectors`. Defaults to `name`.
reducerPath?: string,
// An object of selectors, which receive the slice's state as their first parameter.
selectors?: Record<string, (sliceState: State, ...args: any[]) => any>,
})
例子:
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: (state) => state + 1,
},
})
2.2 createAsyncThunk
一个接受 Redux 操作类型字符串的函数和一个应该返回 promise 的回调函数。通常用于处理异步逻辑。
// 创建一个异步的 Thunk action creator
export const getUserById = createAsyncThunk(
'user/getUserById', // 定义 action 的 type
async (userId) => {
// 异步逻辑,例如从服务器获取数据
const response = await fetchUserById(userId);
// 返回获取到的数据作为 action payload
return response.data;
}
);
2.3 createAction
在 Redux 中定义动作的常用方法是分别声明一个动作类型常量和一个动作创建器函数来构造该类型的动作。与createAsyncThunk区别是:一个同步一个异步。
const INCREMENT = 'counter/increment'
function increment(amount: number) {
return {
type: INCREMENT,
payload: amount,
}
}
const action = increment(3)
// { type: 'counter/increment', payload: 3 }
2.4 createReducer
用于创建 Redux 中的 reducer
-
initialState :第一次调用 reducer 时应使用的初始状态。这也可能是一个“延迟初始值设定项”函数,该函数在调用时应返回初始状态值。每当调用 reducer with 作为其状态值时,都会使用它,并且主要用于从 中读取初始状态等情况。
State | (() => State)
undefined
localStorage
-
builder回调 接收要定义的构建器对象的回调 大小写缩减器通过调用 .
(builder: Builder) => void
builder.addCase(actionCreatorOrType, reducer)
import { createReducer } from '@reduxjs/toolkit';
// 定义初始状态
const initialState = {
value: 0
};
// 使用 createReducer 创建 reducer
const counterReducer = createReducer(initialState, {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
});
export default counterReducer;
3 react-redux
3.1 创建项目
npx create-react-app zhiqu-web-test --template typescript
3.2 useselector
共享状态,从Redux的store中提取数据(state)
const result: any = useSelector(selector: Function, equalityFn?: Function)
selector 在概念上大约等同于 mapStateToProps argument to connect。
selector 将以整个 Redux store state 作为唯一的参数被调用。每当函数组件渲染时,selector 就会被运行(除非在组件的前一次渲染后引用没有改变,这样 hooks 就会返回缓存的结果,而不是重新运行 selector)。useSelector()
也会订阅 Redux store,每当有 action 被 dispatched 时就会运行 selector。
全等比较和更新
默认的对比方式是严格的 ===
引用比较
import { shallowEqual, useSelector } from 'react-redux'
const selectedData = useSelector(selectorReturningObject, shallowEqual)
结合useEffect做全局的store监听:
const channel = useSelector<any, IChannel>((state) => {
return state.channelReducer?.channel as IChannel;
});
useEffect(() => {
...
}, [channel]);
3.3 configureStore
提供了一个简单而灵活的方式来创建 Redux store,使得 Redux 应用的设置过程更加轻松和高效。它是 Redux Toolkit 提供的一个重要工具,可以帮助开发者更快地搭建和管理 Redux 应用。
import { configureStore } from '@reduxjs/toolkit';
// ...
const store = configureStore({
reducer: {
posts: postsReducer,
comments: commentsReducer,
users: usersReducer,
},
});
// 从 store 本身推断出 `RootState` 和 `AppDispatch` types
export type RootState = ReturnType<typeof store.getState>;
// 类型推断: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
综合案例:使用 createSlice、createAsyncThunk 和 configureStore
import { configureStore, createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// 定义一个异步 Thunk action creator
const fetchUserById = createAsyncThunk(
'users/fetchById',
async (userId, thunkAPI) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
return response.json();
}
);
// 定义一个初始状态
const initialState = {
user: {},
loading: false,
error: null,
};
// 创建一个 slice
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
changeUser(state, { payload }) {
state.user= payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchUserById.pending, (state, action) => {
state.loading = true;
})
.addCase(fetchUserById.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload;
})
.addCase(fetchUserById.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});
// 导出 slice 中的 action creators
export const { changeUser } = userSlice.actions;
// 创建 Redux store
const store = configureStore({
reducer: userSlice.reducer,
});
export default store;
//将其分发到 Redux store 中。这样一来,Redux store 中的相应 reducer 将会根据'changeUser' //action 的类型来更新用户信息的状态。
store.dispatch(changeUser(user));
import { type Dispatch } from 'redux';
dispatch(fetchUserById(userId));
在 Redux 应用中,store.dispatch
和 dispatch
都用于触发 action,但它们的使用方式略有不同。
-
store.dispatch: 用于分发 action 到 Redux store 中。它通常在组件外部被调用,比如在 Redux 相关的业务逻辑代码中或者 Redux 中间件中。示例:
store.dispatch(action)
。 -
dispatch:
dispatch
是一个函数,通常通过 React Redux 提供的useDispatch
hook 或者connect
函数的返回值来获取。它用于在 React 组件中分发 action。dispatch
函数本质上是store.dispatch
的封装,用于在组件中直接触发 action,而不需要显式地引用 Redux store。示例:dispatch(action)
。
总的来说,store.dispatch
是 Redux store 实例的方法,用于在 Redux 应用的任意位置触发 action,而 dispatch
是一个函数,通常在 React 组件中使用,用于触发 action 并更新 Redux store 中的状态。
3.4 useDispatch
const dispatch = useDispatch()
这个 hook 返回一个对 Redux store 中的 dispatch
函数的引用。dispatch结合store使用
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
3.5 useStore
为什么很少使用 useStore
?
- 在 React Redux 中,
useStore
允许你从默认的上下文中获取当前的 Redux store 实例。然而,它在实际开发中很少被使用,因为大多数情况下,我们不需要直接访问整个 store。 - 使用
useStore
的主要原因是为了访问 store 的状态或执行一些自定义逻辑。但是,这通常不是最佳实践,因为 React Redux 提供了更高级的 API 来处理 store 交互,例如useSelector
和useDispatch
。
import React from 'react';
import { useStore } from 'react-redux';
const MyComponent = () => {
const store = useStore();
const handleClick = () => {
// 分发一个 action 到 Redux store 中
store.dispatch({ type: 'SOME_ACTION' });
// 获取当前 Redux store 中的状态
const state = store.getState();
console.log(state);
};
return (
<button onClick={handleClick}>Dispatch Action</button>
);
};
export default MyComponent;
3.6 useselector
共享状态,从Redux的store中提取数据(state)
const result: any = useSelector(selector: Function, equalityFn?: Function)
selector 在概念上大约等同于 mapStateToProps argument to connect。
selector 将以整个 Redux store state 作为唯一的参数被调用。每当函数组件渲染时,selector 就会被运行(除非在组件的前一次渲染后引用没有改变,这样 hooks 就会返回缓存的结果,而不是重新运行 selector)。useSelector()
也会订阅 Redux store,每当有 action 被 dispatched 时就会运行 selector。
const channel = useSelector<{ channelReducer: { channel: IChannel } }, IChannel>((state) => {
return state.channelReducer.channel as IChannel;
});
4 redux-immutable
4.1 combineReducers
组合reducer
import { combineReducers } from 'redux-immutable';
const reducer = combineReducers({
loginReducer,
channelReducer,
queueReducer
});
5 react-router-dom
5.1 useNavigate
用于在React应用程序中进行路由导航
const navigate = useNavigate();
navigate('/register', {
replace: false
});
5.2 useLocation
用于获取当前页面的 URL 信息。它返回一个包含当前 URL 信息的 location 对象,包括 pathname、search、hash 等属性。
import React from 'react';
import { useLocation } from 'react-router-dom';
const MyComponent = () => {
// 使用 useLocation hook 获取当前页面的 URL 信息
const location = useLocation();
return (
<div>
<h1>当前页面的 URL 信息:</h1>
<p>Pathname: {location.pathname}</p>
<p>Search: {location.search}</p>
<p>Hash: {location.hash}</p>
</div>
);
};
export default MyComponent;
5.3 路由设置
路由配置:
import { Navigate, type RouteObject } from 'react-router-dom';
import { lazy, Suspense } from 'react';
const routes: RouteObject[] = [
{
path: '/',
element: <Navigate to={LOGIN_PATH} />
}
]
export { routes };
项目启动入口:
import ReactDOM from 'react-dom/client';
import App from './App';
import './assets/css/base.css';
import './constant/format';
const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(<App />);
App.tsx配置:
import { Suspense, useEffect, type ReactElement } from 'react';
import { Provider } from 'react-redux';
import 'antd/dist/reset.css';
import { HashRouter, useRoutes, useLocation, useNavigate } from 'react-router-dom';
import { routes } from './router';
import store from './store';
import './store/actionCreators';
import ErrorBoundary from './components/error';
import { CommonWrapper } from './assets/css/common';
function App(): ReactElement {
function RouteElement(props: any) {
return useRoutes(routes);
}
return (
<CommonWrapper>
<div className="App">
<Provider store={store}>
<HashRouter>
<Suspense fallback={<div>wwww</div>}>
<ErrorBoundary>
<RouteElement />
</ErrorBoundary>
</Suspense>
</HashRouter>
</Provider>
</div>
</CommonWrapper>
);
}
export default App;
通过useRoutes(routes)将路由加载进去
5.4 withRouter
withRouter
是 React Router 提供的一个高阶组件(Higher-Order Component),用于将路由信息注入到组件中。它可以将 React Router 的 history、location 和 match 对象作为 props 传递给被包裹的组件。
import React from 'react';
import { withRouter } from 'react-router-dom';
const MyComponent = ({ history, location, match }) => {
// 在组件中可以访问到 history、location 和 match 对象
return (
<div>
<h1>当前页面的路径:</h1>
<p>Pathname: {location.pathname}</p>
{/* 其他路由信息 */}
</div>
);
};
// 使用 withRouter 高阶组件包裹组件
export default withRouter(MyComponent);
5.5 错误页面定义
import React from 'react';
import { useNavigate } from 'react-router-dom';
function withRouter(WrapperComponent: any) {
return function (props: any) {
const navigate = useNavigate();
const router = { navigate };
return <WrapperComponent {...props} router={router} />;
};
}
export default withRouter;
通过withRouter定义错误页面:
import React, { type ReactElement } from 'react';
import { Button, Result } from 'antd';
import withRouter from '../../hook/withRouter/withRouter';
interface IProps {
children: ReactElement;
router: any;
}
interface IState {
hasError: boolean;
}
class ErrorBoundary extends React.PureComponent<IProps, IState> {
constructor(props: any) {
super(props);
this.state = { hasError: false };
}
//静态方法,用于捕获子组件渲染过程中发生的错误,并更新组件的状态。在这里,如果发生了错误,则将 hasError 状态设置为 true。
static getDerivedStateFromError(error: any) {
return { hasError: true };
}
//生命周期方法,用于捕获子组件渲染过程中发生的错误,并打印错误信息。在这里,将错误信息打印到控制台。
componentDidCatch(error: any, errorInfo: any) {
console.log(error, errorInfo);
}
loginRouter(): void {
const { navigate } = this.props.router;
navigate('/home/recommend');
window.location.reload();
}
render() {
if (this.state.hasError) {
return (
<Result
status="404"
title="错误"
subTitle="抱歉,页面好像出现了一个未知错误,请点击下方按钮跳转至首页"
extra={
<Button
type="primary"
onClick={() => {
this.loginRouter();
}}
>
首页
</Button>
}
/>
);
}
return this.props.children;
}
}
export default withRouter(ErrorBoundary);
6 自定义组件
const getSize = () => {
return window.innerWidth > 1000 ? "large" : "small";
}
const useWindowSize = () => {
const [size, setSize] = useState(getSize());
useEffect(() => {
const handler = () => {
setSize(getSize())
};
window.addEventListener('resize', handler);
return () => {
window.removeEventListener('resize', handler);
};
}, []);
return size;
};
使用:
const Demo = () => {
const size = useWindowSize();
if (size === "small") return <SmallComponent />;
else return <LargeComponent />;
};
7 自定义Context
<Provider>
组件允许你通过 context
prop 指定一个备用的上下文。如果你正在构建一个复杂的、可复用的组件,并且你不希望 store 与 consumer 应用程序可能使用的任何 Redux store 相冲突,那么这很有用。
要通过各种 hook API 访问备用上下文,请使用 hook creator 函数:
import React from 'react'
import {
Provider,
createStoreHook,
createDispatchHook,
createSelectorHook,
} from 'react-redux'
const MyContext = React.createContext(null)
// 如果想在其他文件使用自定义 hook,导出这些自定义 hook。
export const useStore = createStoreHook(MyContext)
export const useDispatch = createDispatchHook(MyContext)
export const useSelector = createSelectorHook(MyContext)
const myStore = createStore(rootReducer)
export function MyProvider({ children }) {
return (
<Provider context={MyContext} store={myStore}>
{children}
</Provider>
)
}