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);