快速开始

这里我们以一个简单的React记数器应用来快速的了解zoro的使用方法,首先让我们看一下计数器实现代码

import React, { Component } from 'react'
import { render } from 'react-dom'

class Counter extends Component {
    state = {
        count: 0,
    }

    handleAdd = () => this.setState({ count: this.state.count + 1 })
    handleReduce = () => this.setState({ count: this.state.count - 1 })

    render() {
        const { count } = this.state

        return (
            <div>
                <div onClick={this.handleAdd}>+</div>
                <div>{count}</div>
                <div onClick={this.handleReduce}>-</div>
            </div>
        )
    }
}

render(<Counter />, rootNode)

这是一个很简易的计数器应用,虽然这样如此小型应用引入redux或者zoro是完全没有必要的,这里仅仅是为了让你对zoro的特性有一个大体的了解

接下来我们一起用zoro来改造这个应用

首先我们需要引入redux与react结合库react-redux,以及zoro库

import { Provider } from 'react-redux'
import zoro from '@opcjs/zoro'

创建redux的store

const app = zoro()
const store = app.start()

将创建好的store注入到应用中

render(<Provider store={store}><Counter /></Provider>, rootNode)

此时应用的创建已经完成,但是这样似乎还没有什么用,我们缺乏redux中状态的处理函数reducer的实现,在zoro中有一个特性叫做model,用于定义数据处理流程,首先我们实现计数器的model,名字就叫counter

export default {
    namespace: 'counter',
    state: {
        count: 0,
    },
    reducers: {
        add(action, state) {
            return { count: state.count + 1 }
        },
        reduce(action, state) {
            return { count: state.count -1 }
        },
    },
}

我解释下model的定义,每一个model都有一个全局唯一的命名空间namespace,后续会介绍这有什么用,model中存放数据在state中,这里的state与redux中的state基本一致,reducers用于定义同步数据处理的action,我们定义了两个action,一个增加计数器1,一个减少计数器1,model的详细定义查看MODEL API

引入model到zoro中

import counter from './models/counter'

const app = zoro()
app.model(counter) // 新增引入counter
const store = app.start()

修改我们的counter组件

import { connect } from 'react-redux'
import { dispatcher } from '@opcjs/zoro'

class Counter extends Component {
    // state = {
    //    count: 0,
    // }

       // handleAdd = () => this.setState({ count: this.state.count + 1 })
    // handleReduce = () => this.setState({ count: this.state.count - 1 })

    handleAdd = () => dispatcher.counter.add()
    handleReduce = () => dispatcher.counter.reduce()

    render() {
        // const { count } = this.state
        const { count } = this.props

        return (
            <div>
                <div onClick={this.handleAdd}>+</div>
                <div>{count}</div>
                <div onClick={this.handleReduce}>-</div>
            </div>
        )
    }
}

const CCounter = connect(({ counter }) => ({
    count: counter.count,
}), null)(Counter)

render(<Provider store={store}><CCounter /></Provider>, rootNode)

就此,一个完整的改造就完成了,在上面的例子中,你可能注意到了dispatcher,这是一个action的触发器,比如我们想要出发计数器加一,我们可以调用dispatcher.counter.add(),这里的counter就是前面model定义时的namespace

完整改造代码如下

./model/counter.js

export default {
    namespace: 'counter',
    state: {
        count: 0,
    },
    reducers: {
        add(action, state) {
            return { count: state.count + 1 }
        },
        reduce(action, state) {
            return { count: state.count -1 }
        },
    },
}

./index.js

import React, { Component } from 'react'
import { render } from 'react-dom'
import { Provider, connect } from 'react-redux'
import zoro, { dispatcher } from '@opcjs/zoro'

import counter from './models/counter'

const app = zoro()
app.model(counter)
const store = app.start()

class Counter extends Component {
    handleAdd = () => dispatcher.counter.add()
    handleReduce = () => dispatcher.counter.reduce()

    render() {
        const { count } = this.props

        return (
            <div>
                <div onClick={this.handleAdd}>+</div>
                <div>{count}</div>
                <div onClick={this.handleReduce}>-</div>
            </div>
        )
    }
}

const CCounter = connect(({ counter }) => ({
    count: counter.count,
}), null)(Counter)

render(<Provider store={store}><CCounter /></Provider>, rootNode)

异步数据流

上面我们介绍了如何通过zoro处理同步数据流的问题,那我们的异步数据流该怎么处理呢

异步数据流的处理主要依靠model的effect,这是一个封装过后的redux异步中间件,支持async,await语法

我们扩展一下上面计数器的功能,在计数器加减之前需要去后台请求获取可变化的计数范围

修改counter model

import axios from 'axios'

export default {
    namespace: 'counter',
    state: {
        count: 0,
        max: 0, // 新增max数据
    },
    effects: {
        // 新增请求最大计数值的异步action
        async queryCounterRange(action, { put }) {
            const { max = 0 } = await axios.get('serverHost/couter/range')
            put({ type: 'update', payload: { max } })
        },
    },
    reducers: {
        // 新增update同步action
        update({ payload }, state) {
            return { ...state, ...payload }
        },
        add(action, state) {
            return { count: state.count + 1 }
        },
        reduce(action, state) {
            return { count: state.count -1 }
        },
    },
}

我们新增了一个异步action,queryCounterRange,他会请求后台服务,获取可增加的最大值,并通过update action存储到state中供界面使用

修改counter组件

import React, { Component } from 'react'
import { render } from 'react-dom'
import { Provider, connect } from 'react-redux'
import zoro, { dispatcher } from '@opcjs/zoro'

import counter from './models/counter'

const app = zoro()
app.model(counter)
const store = app.start()

class Counter extends Component {
    componentDidMount() {
        // 页面初始化后触发请求计数最大值
        dispatcher.counter.queryCounterRange()
    }
    handleAdd = () => {
        // 增加计数值判定
        const { count, max } = this.props
        if (count < max) {
            dispatcher.counter.add()
        }
    }
    handleReduce = () => {
        // 增加计数值判定
        const { count } = this.props
        if (count > 0) {
            dispatcher.counter.reduce()
        }
    }
    render() {
        const { count } = this.props

        return (
            <div>
                <div onClick={this.handleAdd}>+</div>
                <div>{count}</div>
                <div onClick={this.handleReduce}>-</div>
            </div>
        )
    }
}

const CCounter = connect(({ counter }) => ({
    count: counter.count,
    // 增加最大计数值导入
    max: counter.max,
}), null)(Counter)

render(<Provider store={store}><CCounter /></Provider>, rootNode)

更多特性请查看API文档

results matching ""

    No results matching ""