使用 umi + dva 搭建 react 项目

起步

首先通过 umi 快速创建项目

mkdir myapp && cd myapp
yarn create @umijs/umi-app
yarn

启动项目

yarn start

路由

  1. 通过配置文件配置路由
    直接在.umirc.ts 的 routes 中配置路由
export default {
  routes: [
    { exact: true, path: '/', component: 'index' },
    { exact: true, path: '/user', component: 'user' },
  ],
}
  1. 如果 .umirc.ts 里没有 routes 那么文件系统即路由,通过目录和文件及其命名分析出路由配置

  2. 动态路由
    约定 [] 包裹的文件或文件夹为动态路由。

比如上面的目录最后会被动态编译成 /test/:id 路由,所以我们通过 props.match.params 拿到的就是 id

  1. 动态可选路由
    约定 [ $] 包裹的文件或文件夹为动态可选路由

比如:

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容