起步
首先通过 umi 快速创建项目
mkdir myapp && cd myapp
yarn create @umijs/umi-app
yarn
启动项目
yarn start
路由
- 通过配置文件配置路由
直接在.umirc.ts
的 routes 中配置路由
export default {
routes: [
{ exact: true, path: '/', component: 'index' },
{ exact: true, path: '/user', component: 'user' },
],
}
如果
.umirc.ts
里没有routes
那么文件系统即路由,通过目录和文件及其命名分析出路由配置动态路由
约定 [] 包裹的文件或文件夹为动态路由。
比如上面的目录最后会被动态编译成 /test/:id 路由,所以我们通过 props.match.params 拿到的就是 id
- 动态可选路由
约定 [ $] 包裹的文件或文件夹为动态可选路由
比如:
src/pages/users/[id$].tsx 会成为 /users/:id?
src/pages/users/[id$]/settings.tsx 会成为 /users/:id?/settings
这样就算直接访问/test 或者 访问/test/:id都不会报错
安装 dva
yarn add dva
这里需要注意每个目录下面必须有一个service和model文件,具体代码见
https://github.com/wanglifa/umi-dva-react/tree/9a3a403a8d35e242ef77e04d068d3f3428a9dbff
reducer 和 effect 的用法
dva里想要修改 modal 里的 state,只能通过 reducer 来修改,如果是同步的直接通过 dispatch 触发 reducer,如果需要异步修改那么我们就在effect里,成功后调用 reducer
1). 通过 effect 调用接口成功后修改 state
export default {
namespace: 'users',
state: {
list: [],
total: null,
},
reducers: {
modifyList(state, { payload }) {
return {...state, ...payload}
},
},
effects: {
*_getList({payload, callback}, { call, put }) {
const response = yield call(getList, { payload });
console.log(response)
if (response.code === 200) {
if (callback && typeof callback === 'function') {
callback(response); // 返回结果
// 调用接口成功后修改 state
yield put({
type: 'modifyList',
payload: {list: response.data}
})
}
}
},
},
};
使用 dispatch 触发
useEffect(() => {
dispatch({
type: 'users/save',
payload: {
data: [{name: 'lifa'}],
total: 2
},
callback: (res: any) => {
console.log(res)
},
})
}, [])
2). 直接通过 reducer 修改 state
import React, {memo} from 'react;
import { connect } from 'dva';
const Test = (props) => {
const { users } = props;
const handleModifyList = () => {
dispatch({
type: 'users/modifyList',
payload: {
// 修改 state 里的 list 的值
list: [{a: 1}]
}
})
}
return (
<>
<div>{users.list[0]}</div>
<Button onClick={handleModifyList}>点我</Button>
</>
)
}
export default connect(({users}) => ({users}))(memo(Test))
何时使用 state
1). 对于没有父子关系的组件
2). 层级很深的父子组件,一个属性传了超过两次的时候
如何使用
1). 引入 connect
2). connect 的第一个参数为我们需要拿到 state 的 modal 的namespace
connect(({users}) => ({users}))
3). 通过 props.modal里的namespace获取对应的state
如
props.users.list
使用useSelector useDispatch 替代connect
- seSelector()
const result : any = useSelector(selector : Function, equalityFn? : Function)
主要作用:
从redux的store对象中提取数据(state)。
注意:选择器函数应该是纯函数,因为它可能在任意时间点多次执行。
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}
- useDispatch()
const dispatch = useDispatch()
返回Redux store中对dispatch函数的引用。你可以根据需要使用它。
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>
)
}
将dispatch传递给子组件时,建议使用useCallback来进行回调,否则,由于更改了引用,子组件可能会多次 render。
- useStore()
const store = useStore()
这个Hook返回redux <Provider>组件的store对象的引用。
这个钩子应该不长被使用。useSelector应该作为你的首选。但是,有时候也很有用。来看个例子:
import React from 'react'
import { useStore } from 'react-redux'
export const CounterComponent = ({ value }) => {
const store = useStore()
// 仅仅是个例子! 不要在你的应用中这样做.
// 如果store中的state改变,这个将不会自动更新
return <div>{store.getState()}</div>
}
dva 中使用
原始写法
- model.js
export default {
namespace: 'user',
state: {
userInfo:null,
},
effects: {
*fetchUser({paylaod},{call,put}){
const res = yield(api,payload)
yield put({
type: 'save',
payload: {
userInfo:res
},
});
}
},
reducers:{
save(state, { payload }) {
return {
...state,
...payload,
};
},
}
}
在页面中使用
import {connect} from 'dva'
const Home = props=>{
// 获取数据
const {user,loading,dispatch} = props
// 发起请求
useEffect(()=>{
dispatch({
type:'user/fetchUser',payload:{}
})
},[])
// 渲染页面
if(loading) return <div>loading...</div>
return (
<div>{user.name}<div>
)
}
export default connect(({loading,user})=>({
loading:loading.effects['user/fetchUser'],
user:user.userInfo
}))(Home)
使用 useDispatch useSelector 优化上面的代码
import {useDispatch,useSelector} from 'dva'
const Home = props=>{
const dispatch = useDispatch()
const loadingEffect = useSelector(state =>state.loading);
const loading = loadingEffect.effects['user/fetchUser'];
const user = useSelector(state=>state.user.userInfo)
// 发起请求
useEffect(()=>{
dispatch({
type:'user/fetchUser',payload:{}
})
},[])
// 渲染页面
if(loading) return <div>loading...</div>
return (
<div>{user.name}<div>
)
}
export default Home