react入门(中)
自定义hook
import { useState } from 'react'
// 把可复用的逻辑写在一个自定义的 hook 中,最后返回设定的值和方法,可以以数组或是对象的形式返回
const useToggle = () => {
const [value, setValue] = useState(false)
const toggle = () => setValue(!value)
return [value, toggle]
}
const App = () => {
// 使用自定义 hook
// 调用 useToggle 返回的值和方法
const [value, toggle] = useToggle()
return (
<div>
<button onClick={toggle}>Toggle</button>
<div>{value && 'Hello World'}</div>
</div>
)
}
export default App
状态管理
redux
可以理解为vue中的vuex和pinia,进行集中式状态管理
redux toolkit 官方推荐redux逻辑的方式,是一套工具集,简化书写方式
react-redux 用来链接react组件和redux的中间件
安装插件
pnpm i @reduxjs/toolkit react-redux
定义 store
// src/store/modules/user.tsx
import { createSlice } from '@reduxjs/toolkit';
// 创建一个包含初始状态、同步方法的 slice
const userSlice = createSlice({
name: 'user',
initialState: { token: '00002' },
reducers: {
setToken: (state, action) => {
state.token = action.payload;
},
delToken: (state) => {
state.token = '';
},
},
});
// 解构出创建 action 对象的函数
const { setToken, delToken } = userSlice.actions;
// 获取 reducer 函数
const userReducer = userSlice.reducer;
// 导出 action 和 reducer
export { setToken, delToken };
export default userReducer;
中转配置 store
// src/store/index.tsx
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './modules/user';
// 配置 Redux store
// 根 store 组装所有子模块
const store = configureStore({
reducer: {
user: userReducer,
},
});
export default store;
注入 store
# main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import {RouterProvider} from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store'
import router from './router'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
</React.StrictMode>
)
组件中使用store
import { useSelector, useDispatch } from 'react-redux'
import { setToken, delToken } from '../../store/modules/user'
function App() {
const { token } = useSelector((state: any) => state.user)
const dispatch = useDispatch()
return (
<>
<span>{token}</span>
<br />
<button onClick={() => dispatch(setToken('123'))}>设置token</button>
<button onClick={() => dispatch(delToken())}>删除token</button>
</>
)
}
export default App
Zustand
安装
pnpm i zustand
定义 store
// 从 zustand 库导入 create 函数,用于创建新的 store 实例
import { create } from 'zustand';
// 使用 create 函数创建一个新的 store 实例
const userStore = create((set) => {
// 返回一个对象,作为 store 的初始状态和方法
return {
// store 的初始状态,token 被初始化为 '0002'
token: '0002',
// 定义一个方法 setToken,接收一个 payload 参数,并使用 set 函数更新 token 的值
setToken: (payload) => set({ token: payload }),
// 定义一个方法 delToken,不接收任何参数,调用后将 token 更新为空字符串 ''
delToken: () => set({ token: '' })
}
})
// 将 userStore 导出为默认导出,以便在其他模块中使用
export default userStore;
组件中使用
import userStore from '../../store/modules/user'
function App() {
const { token, setToken, delToken } = userStore()
return (
<>
<span>{token}</span>
<br />
<button onClick={() => setToken('123456')}>设置token</button>
<button onClick={() => delToken()}>删除token</button>
</>
)
}
export default App
切片模式
待施工
Easy-peasy
建立在redux
之上,看单词就能看出来,翻译过来就是非常简单,小菜一碟
直接上手,安装插件
pnpm i easy-peasy
我们在根目录新建一个store
文件夹,再新建一个index.tsx
对所有的状态进行中转,再新建一个modules
对各自的状态进行管理
src/
|-- store/
| |-- index.js
| |-- modules/
| |-- user.js
|-- App.js
action 用于定义同步状态
- action 回调 中的两个参数 state, payload
- 第一个参数 state 是当前数据的状态,可以直接修改来更新状态
- 第二个参数 payload 是传递给 action 的数据
thunk 用于定义异步状态
- thunk 回调 中有两个参数 actions, payload
- 第一个参数 actions 是当前模块的 action,可以调用 action 来更新状态
- 第二个参数 payload 是调用异步函数传递给 thunk 的数据
定义
# user.tsx
import { action, thunk } from 'easy-peasy'
import request from '@/utils/request'
// 定义 user 模块
const user = {
token: '00002',
setToken: action((state, payload) => {
state.token = payload
}),
delToken: action(state => {
state.token = ''
}),
getToken: thunk(async actions => {
const { data } = await request.get('/get/token')
actions.setToken(data)
}),
}
export default user
中转
# index.tsx
import { createStore } from 'easy-peasy';
import user from './modules/user'
/**
* 在 index.tsx 中导入createStore 方法创建一个 store 实例
*/
// 组合所有模块
const storeModel = {
user:user
}
// 创建 store 实例存储
const store=createStore(storeModel)
export default store;
组件中使用
useStoreState
和 useStoreActions
都接收一个回调函数。这种方式允许你访问和使用 store 的状态或 actions,而不是直接暴露整个 store 对象。这样可以避免不必要的重新渲染,确保组件仅在相关状态或 actions 发生变化时才更新
import { useStoreState, useStoreActions } from 'easy-peasy' // 在要使用的组件中导入 useStoreState 和useStoreActions hook 来访问和操作 store 中的状态
const home = () => {
const token = useStoreState((state)=> state.user.token)
const setToken = useStoreActions((state)=> state.user.setToken)
const delToken = useStoreActions((state)=> state.user.delToken)
return (
<>
<div>{token}</div>
<button onClick={()=>setToken('123456')}>设置token</button>
<br />
<button onClick={()=>delToken()}>清除token</button>
</>
)
}
export default home
ReactRouter
安装 react-router-dom 包
pnpm i react-router-dom
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom' // 引入路由组件
import router from './router' // 引入路由实例
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router}></RouterProvider> RouterProvider 用来提供路由出口并绑定路由
</React.StrictMode>
)
配置路由信息
import { createBrowserRouter } from 'react-router-dom'
import Home from '../pages/home'
import Article from '../pages/article'
创建路由实例对象
const router = createBrowserRouter([
{
path: '/',
element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
},
{
path: '/article',
element: <Article />,
},
])
// 导出路由实例对象
export default router
路由跳转
编程式导航
使用 useNavigate
可以理解为 vue 中的 router.push()方法
import { Link , useNavigate} from "react-router-dom"
const Home = () => {
const navigate = useNavigate()
return (
<div>
<Link to={"/article"}>article</Link>
<h1>home</h1>
<button onClick={() => navigate('/article')}>去文章页面</button>
</div>
)
}
export default Home
声明式导航
使用 Link 标签 to 属性后为跳转的路径地址,在实际渲染时会被渲染为 html 的 a 标签
import { Link } from "react-router-dom"
const Home = () => {
return (
<div>
<Link to={"/article"}>article</Link>
<h1>home</h1>
</div>
)
}
export default Home
路由传参
searchParams传参
// 首页
import { useNavigate, Link } from 'react-router-dom'
const home = () => {
const navigate = useNavigate()
return (
<div>
<h1>Home</h1>
<button onClick={() => navigate('/article?id=1001&name=react')}>去文章页</button>
</div>
)
}
export default home
// 详情页
import { Link } from 'react-router-dom'
import { useSearchParams } from 'react-router-dom'
// 使用了useSearchParams钩子来获取URL中的查询参数。useSearchParams返回一个数组,其中第一个元素是SearchParams对象,第二个元素是一个函数,用于更新查询参数
const Article = () => {
const [params] = useSearchParams()
const id = params.get('id') // 通过get方法获取参数
const name = params.get('name')
return (
<div>
<Link to='/home'>回到首页</Link>
<h1>article</h1>
<p>id:{id}--name:{name}</p>
</div>
)
}
export default Article
params传参
// 首页
import { useNavigate, Link } from 'react-router-dom'
const home = () => {
const navigate = useNavigate()
return (
<div>
<h1>Home</h1>
<button onClick={() => navigate('/article/1002/react')}>去文章页</button>
</div>
)
}
export default home
去到路由的位置,加上占位符
const router = createBrowserRouter([
{ path: '/article/:id/:tile', element: <Article /> }
])
详情页
import { Link,useParams } from 'react-router-dom'
const Article = () => {
const params = useParams()
const id = params.id // params 是通过.语法来获取传递的参数
const name = params.name
return (
<div>
<Link to='/home'>回到首页</Link>
<h1>article</h1>
<p>id:{id}----name:{name}</p>
</div>
)
}
export default Article
嵌套路由
使用children表示对应的二级路由地址
import { createBrowserRouter } from 'react-router-dom'
import Home from '../pages/home'
import Article from '../pages/article'
// 创建路由实例对象
const router = createBrowserRouter([
{
path: '/',
element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
children: [
{
path: 'article',
element: <Article />,
}
]
},
])
// 导出路由实例对象
export default router
在要渲染二级路由的地方,放置 Outlet 标签,和vue中的 view-router 是一个意思
import { Link , Outlet} from "react-router-dom" // outlet 表示占位符,表示二级路由的渲染位置
const Home = () => {
return (
<div>
<Link to='/article'>article</Link>
<h1>home</h1>
<Outlet/>
</div>
)
}
export default Home
默认显示二级路由
去掉二级路由的 path 属性替换为 index:true
import { createBrowserRouter } from 'react-router-dom'
import Home from '../pages/home'
import Article from '../pages/article'
// 创建路由实例对象
const router = createBrowserRouter([
{
path: '/',
element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
children: [
{
index: true, // index 表示当前路由是默认路由
element: <Article />,
}
]
},
])
// 导出路由实例对象
export default router
404 页面配置
import { createBrowserRouter } from 'react-router-dom'
import Home from '../pages/home'
import Article from '../pages/article'
// 创建路由实例对象
const router = createBrowserRouter([
{
path: '/',
element: <Home />, // element 表示要渲染的组件 ,就是 vue 中的component
children: [
{
index: true, // index 表示当前路由是默认路由
element: <Article />,
}
]
},
{
path: '*',
element: <div>404</div> // 表示 404 页面
}
])
// 导出路由实例对象
export default router
路由懒加载
- 使用 react 内置的lazy函数实现路由懒加载
- Suspense 组件包裹路由中element选项对应的组件
import { createBrowserRouter } from 'react-router-dom'
import { lazy, Suspense } from 'react'
const Detail = lazy(() => import('../pages/detail/index'))
const Home = lazy(() => import('../pages/home/index'))
const router = createBrowserRouter([
{
path: '/',
element: (
<Suspense>
<Home />
</Suspense>
),
children: [
{
path: 'detail',
element: (
<Suspense>
<Detail />
</Suspense>
),
},
],
}
])
export default router
转载自:https://juejin.cn/post/7396361501730914342