create-react-app结合dva.js1-搭建项目整合dva.js

create-react-app结合dva使用

dva: dva 首先是一个基于redux和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

dva数据流向图

create-react-app结合dva.js1-搭建项目整合dva.js_第1张图片

创建项目

运行npx create-react-app dva_project,
创建项目完成后 弹出配置npm run eject

导入dva.js

安装dva

cnpm install dva --save

在src目录下新建views文件夹, 用来存放页面组件

在views文件夹下, 新建一个Page1.jsx文件组件, 用来显示页面

里面代码:

import React, {
      Component } from 'react';

class Page1 extends Component {
     
    constructor(props) {
     
        super(props);
        this.state = {
       }
    }
    render() {
      
        return ( 
            <div>
                page1
            </div>
         );
    }
}
 
export default Page1;

创建model层,存放数据

在src新建model文件夹, 用来存放数据state

在model文件夹下新建 global.js 用来存放全局的state, 这个全局的state每个组件都可以用到

const initState = {
      text: "我是全局的state" }

export default {
     
    namespace: "global",
    state: initState,
    subscriptions : {
     },
    effects: {
     },
    reducers: {
     },
}

同时新建一个page1.js 用来存放Page1组件的state

const initState = {
      count: 1 }

export default {
     
    namespace: 'page1',
    state: initState,
    subscriptions: {
     },
    effects: {
     },
    reducers: {
     },
  }

在一个model的文件中:

  • namespace :model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持通过 . 的方式创建多层命名空间。

  • state: 初始值,优先级低于传给 dva()opts.initialState

  • reducers: 以 key/value 格式定义 reducer。用于处理同步操作,唯一可以修改 state 的地方。由 action 触发。格式为 (state, action) => newState[(state, action) => newState, enhancer]

  • effects: 以 key/value 格式定义 effect。用于处理异步操作和业务逻辑,不直接修改 state。由 action 触发,可以触发 action,可以和服务器交互,可以获取全局 state 的数据等等。

    格式为 *(action, effects) => void[*(action, effects) => void, { type }]

  • subscriptions: 以 key/value 格式定义 subscription。subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。在 app.start() 时被执行,数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。

    格式为 ({ dispatch, history }, done) => unlistenFunction

创建路由

dva.js中内置了react-router, 所有我们不用再自己安装react-router了, 但是我们会用到history库.

安装 history

cnpm install history --save 在index中导入路由会用到

在src下新建route文件夹, 用来存放路由文件

在route文件夹下新建router1.js文件, 为了适配多人开发, 可能会有多个路由配置文件

const Page1 = () => import('../views/Page1')

let route = [{
     
    path: '/page1',
    models: () => [import('../model/page1')], // 可以导入多个
    component: Page1
}]

export default route
在route文件夹下面新建 index.jsx 用来存放router组件
import React from 'react';
import {
      Router, Route, Switch } from 'dva/router';
import dynamic from 'dva/dynamic'

import router1 from './router1'

const allRouter = [...router1]  // 多个router可以在这个地方整合

function RouterCompontent({
      history, app }) {
     
    return (
        <Router history={
     history}>
            <Switch>
                {
     
                    allRouter.map(({
      path, ...dynamics }, index) => (
                        <Route key={
     index} path={
     path} exact component={
     dynamic({
     app, ...dynamics})} />
                    ))
                }
            </Switch>
        </Router>
    )
}

export default RouterCompontent

实现多层路由,子路由,嵌套路由请点击

修改项目入口文件: src下面的index.js

import dva from 'dva';
import './index.css'

const createHistory = require("history").createHashHistory // 这里使用的hash路由模式

// 创建应用
const app = dva({
      history: createHistory() })

// 注册model, 会这样会自动导入global的model
app.model(require('./model/global').default)

// 注册路由
app.router(require('./route/index').default)

// 启动应用
app.start('#root')

如果使用history模式路由, 上面第四行要改成这样

const createHistory=require("history").createBrowserHistory   // 使用history路由模式

删除没有用的文件 app.jsapp.css等, 这个时候基本的配置工作已经完成了

启动项目

运行npm start

这个时候访问 http://localhost:3000/#/page1 可以看到 页面上 会有page1

create-react-app结合dva.js1-搭建项目整合dva.js_第2张图片
这个时候我们在Page1.jsx中打印一下this

    componentDidMount() {
     
        console.log(this)
    }

会发现
create-react-app结合dva.js1-搭建项目整合dva.js_第3张图片

并没有我们要的state数据, 别急, 接下来我们操作,

在Page1.jsx中导入:

import {
      connect } from 'dva'

然后

const mapStateToProps = state => state
export default connect(mapStateToProps)(Page1)

这样写 state中可能会有上个页面或者组件用到的state

如果想明确的注入自己的state, 可以使用结构赋值这样写(推荐使用):

const mapStateToProps = ({
      global,  page1 }) => ({
      global, page1 })
export default connect(mapStateToProps)(Page1);

这样写的好处是, 不会有其他的state干扰, 其中global是默认全局的model,page1是组件自己的model

现在, Page1.jsx是这样

import React, {
      Component } from 'react';
import {
      connect } from 'dva'

class Page1 extends Component {
     
    constructor(props) {
     
        super(props);
        this.state = {
     }
    }

    componentDidMount() {
     
        console.log(this)
    }

    render() {
     
        return (
            <div>
                page1
            </div>
        );
    }
}

const mapStateToProps = ({
      global,  page1 }) => ({
      global, page1 })
export default connect(mapStateToProps)(Page1);

再次打印this

可以看到
create-react-app结合dva.js1-搭建项目整合dva.js_第4张图片
这次 this.props中有了我们想要的global和page1中的state

我们可以直接在page1中使用上面传入的state了, 当然这里只能使用, 不能直接修改state中的数据

import React, {
      Component } from 'react';
import {
      connect } from 'dva'

class Page1 extends Component {
     
    constructor(props) {
     
        super(props);
        this.state = {
     }
    }

    componentDidMount() {
     
        console.log(this)
    }

    // 按钮点击事件
    btnClickHandler() {
     

    }

    render() {
     
        const {
      global, page1 } = this.props
        return (
            <div>
                {
     /* 这里就是为了展示一下全局的state, 没啥特殊含义 */}
                <div>{
     global.text}</div>
                {
     /* 我们实现点击按钮给page1.count 加10 */}
                <div>{
     page1.count}</div>
                <button onClick={
     this.btnClickHandler.bind(this)}>点击+10</button>
            </div>
        );
    }
}

const mapStateToProps = ({
      global,  page1 }) => ({
      global, page1 })
export default connect(mapStateToProps)(Page1);

修改model中的数据

我们要实现一个点击按钮, 页面上显示的count加10的功能, 现在页面变成了这样
create-react-app结合dva.js1-搭建项目整合dva.js_第5张图片
点击按钮还没有反应, 别急, 我们要给model中的page1.js添加一些事件:

我们要先在reducers中定义一个可以修改state的方法

        countIncrease(state, {
      payload }) {
     
            return {
      ...state, count: state.count + payload }
        }

reducers唯一可以修改 state 的地方。由 action 触发。

effects中添加

        * changeCount({
      payload }, {
      put }) {
     
            yield put({
      type: "countIncrease", payload})
        }

effects方法中的put 方法可以直接触发reducers中的方法, 这个在发送异步请求是会经常用到, 如果只是单纯的改变数据, 没有异步操作, 也可以使用dispatch触发reducers中的方法来改变state

现在modelpage1.js变成这样

const initState = {
      count: 1 }

export default {
     
    namespace: 'page1',
    state: initState,
    subscriptions: {
     },
    effects: {
     
        * changeCount({
      payload }, {
      put }) {
     
            yield put({
      type: "countIncrease", payload})
        }
    },
    reducers: {
     
        countIncrease(state, {
      payload }) {
     
            return {
      ...state, count: state.count + payload }
        }
    }
}

page1.jsx中的btnClickHandler中写:

    btnClickHandler() {
     
        const {
      dispatch } = this.props
        dispatch({
     
            type: "page1/changeCount",
            payload: 10
        })
    }

使用dispatch来触发一次action, type的值是model命名空间/空间中的方法, 方法可以是effects或者reducers中定义的方法

payload是有效载荷, 也就是调用方法要传递的数据

再次打开页面
create-react-app结合dva.js1-搭建项目整合dva.js_第6张图片
这次再点击就能发现页面的count发生了变化!

小问题

启动项目的时候页面会报一个警告
在这里插入图片描述
这个是由于引入的dva中的代码抛出的警告, 但是不影响使用, 想要消除这警告, 需要修改dva包中的源码, 具体方法可以百度一下 ,但是修改的意义不大,每次重新导入包的时候, 这个警告就会再次出来!

下一篇: 结合dva.js封装axios请求

你可能感兴趣的:(react,dva,reactjs,前端)