Redux 入门

作者: 写代码的海怪 | 来源:发表于2018-12-20 07:07 被阅读21次

学习 Redux 是个十分痛苦的过程,因为你有可能不知道 Redux 和 React-Redux 是两个不同的东西,而且一堆看起来很新的概念总能把新手绕晕。

这篇文章将从一个简单的例子开始讲 Redux 到底要怎么使用。建议先学习 EventHub 或者看我的另一篇文章 React 的兄弟组件通信,因为 Redux 的主要思想就是 EventHub 思想。

纯 JS + Redux

先说下需求,首先要有一个数字,点击按钮数字加1,没了。不过这个过程要使用到 Redux,也就是说这个数字要放在全局变量中。

这里就用一个简单的 index.html 写就完了。HTML 代码如下:

<div id="app"></div>

接下来写 JS。

function add() {
    store.dispatch({ type: 'add', payload: 1 }) // 1. dispatch 一个 action
}

function render() {
    let app = document.querySelector('#app')
    app.innerHTML = `
    Clicked: <span id="value">${store.getState()}</span> times
    <div>
        <button id="add" onclick="add1()">+1</button>
    </div> `
}

function stateChanger(state, action) {
    if (state === undefined) {
       return 0
    }
    else {
        if (action.type === 'add') {
            let newState = state + action.payload
            return newState // 2. 根据操作生成新的 state 触发一个事件
        }
    }
}

let store = Redux.createStore(stateChanger)

render(store)
store.subscribe(render) // 3. 接收到事件,重新 render

这里说明一下运行的步骤:

  1. 首先运行 render() 将标签都 append 到 document 上,并在按钮上绑定 add 回调
  2. 点击了按钮执行 add() ,然后 dispatch 一个 action(相当于触发了名为“add”的一个事件)
  3. 因为在 Redux.createStore(stateChanger) 时,Redux 会监听 action(也就是事件)的触发,所以会执行 stateChanger 里的代码
  4. 因为在 store.subscribe(render) 在 if-else 里修改了新的 state 后,Redux 会重新执行 render() 函数,从而更新整个页面

Redux 的用法就是这么简单!但是为什么我们看官方档看得想死呢?因为加了 React,后来的所有看起来很变态的使用方法都是为了解决:怎么让代码更好分离,怎么获取全局变量等。思想还是那个思想。

React + Redux

现在我们正式加入 React,使用官方提供的create-react-app来创建应用。App 组件写成这样:

// App.js
class App extends Component {
    add1() {
        this.props.store.dispatch({  // 1. dispatch 一个 action
            type: 'add',
            payload: 1
        })
    }
    render() {
        return (
            <div className="App">
                Clicked: <span id="value">{this.props.value}</span> times
                <div>
                    <button id="add1" onClick={this.add1.bind(this)}>+1</button>
                </div>
            </div>
        )
    }
}

export default App

在入口文件去初始化 Redux:

const stateChanger = (state, action) => {
    if (state === undefined) {
        return 0
    }
    else {
        if (action.type === 'add') {
            let newState = state + action.payload
            return newState // 2. 根据操作生成新的 state 触发一个事件
        }
        else {
            return state
        }
    }
}

function render() {
    ReactDOM.render(
        <App
            value={store.getState()}
            store={store}
        />,
        document.getElementById('root')
    )
}

const store = createStore(stateChanger)

store.subscribe(render) // 3. 接收到事件,重新 render

render()

感觉好像没什么变化呀,不就将一个 index.html 分成了 index.js 和 App.js 么?但是这里涉及到怎么去获取 store 的问题。

如果一两个组件就用 props 来传 store 就好了,但是如果组件很深,那么 store 就要像传家保一样一层层往下传,十分麻烦。

为了解决这个问题, React 社区又推出了一个新的工具:React-Redux。注意这个和 Redux 没有太大关系。为了说明这两个是不一样,我放下他们的官网:Redux 官网React-Redux 官网

React + Redux + React-Redux

先说明这个工具就只是用来让别的组件可以访问到 store 而已,所以它只有 4 个API,其中主要我们要用的就 2 个:Provider, connect。简单理解:

  1. Provider:将 store 放在顶层组件
  2. connect:将 store 里的数据和 dispatch action (也就是触发事件)和当前组件绑定,使得该组件可以自由访问和修改 store

现在修改上面两个文件:

// index.js
const stateChanger = (state, action) => {
    if (state === undefined) {
        return { n: 0 }
    }
    else {
        if (action.type === 'add') {
            let newState =  {
                n: state.n + action.payload
            }
            return newState
        }
        else {
            return state
        }
    }
}

const store = createStore(stateChanger)

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
)

下面的 App.js

class App extends Component {
    render() {
        return (
            <div className="App">
                Clicked: <span id="value">{this.props.n}</span> times
                <div>
                    <button id="add" onClick={this.props.add.bind(this)}>+1</button>
                </div>
            </div>
        )
    }
}

// 将部分 store 里的 state 映射到 props 上
function mapStateToProps(state) {
    return {
        n: state.n
    }
}

// 将 dispatch action 相关操作映射到 props 上
function mapDispatchToProps(dispatch) {
    return {
        add: () => {
            dispatch({ type: 'add', payload: 1 })
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

来对比一下两份代码:

少了哪些?

  1. 不用 store.subscribe(render),Provider 会在更新值后自动重新渲染
  2. 不用在组件 App 里写回调函数了,在 mapDispatchToProps 里声明事件,这个事件会就负责调用 dispatch(action)

多了哪些?

  1. 要在 App 组件外再去套一层 <Provider/>
  2. 将 App 组件和 state,调用 dispatch 的函数连接起来。连接(映射)后的结果是,所有 state 数据和调用 dispatch 的函数都可以从 this.props 得到

这样就解决了组件访问,修改 store 里的 state 问题。唉,绕了一大圈就为了搞这些事。

那些变态概念

Redux 为什么这么难,是因为它将很简单的概念给了个新名字罢了。

Redux

  1. Store:存放全局数据的一个东西,但是要通过 store.getState() 来获取全局数据
  2. State:全局数据
  3. Action:对应 EventHub 里的事件,actionType 就是事件名,payload 就是传入的数据
  4. Dispatch:触发事件,如 dispatch({ type: 'add', payload: 1}),就是指触发一个名为“add”的事件,并带上了数据 1
  5. Reducers:对应触发事件后的回调函数,如我们平时听到的onError = xxx,这个 xxx 就是 Reducer,这个例子的 Reducer 就是 stateChanger

React-Redux

  1. Provider:可以看成一个包住最外面 App 的一个标签,这个标签传入 store 后,通过某些方法所有组件都可以访问到 store
  2. mapStateToProps:将存在 store 里的数据放在这个组件的 props 上
  3. mapDispatchToProps:将需要调用 dispatch 的函数放在这个组件的 props 上
  4. connect:将上面两个东西和这组件联系起来

是不是很烦,嗯,我也是这么觉得的,明明很简单的概念,就是不好好说话。
(完)

相关文章

网友评论

    本文标题:Redux 入门

    本文链接:https://www.haomeiwen.com/subject/updekqtx.html