一、生命周期
1,初始化的执行顺序,初始生命周期执行过程详解
react 本质上是一个 JavaScript 的库,是创建UI接口的视图层框架
(图一)
如图一所示,假如蓝色组件需要和灰色组件通信,只使用 react 视图层框架,就需要调用父组件函数的形式通信,逐层往父级通信
但对于大型应用来说,这样实现基本不太可能,过多的组件会造成维护困难,那应该怎么做呢?
这个时候就应该在 react 视图层框架上配套一个数据层框架 — Redux ,结合应用
redux 要求我们把数据都放在 store 公共存储空间,当绿色组件想要去传递数据时,只需要改变 store 里边对应的数据,灰色区域会自动感知到 store 有变化,就会重新去 store 取数据,从而灰色组件就能得到新的数据
这样的操作流程对于深层次的组件是非常适用的,组件与组件之间的数据传递会变得非常简单
组件改变,修改数据,其他组件再来取值。这就是 Redux 的基础设计理念
使用 Redux-thunk 中间件进行ajax请求发送
如果我们把这种异步的请求,或者把一些非常复杂的逻辑都放在组件里进行实现时,这个组件会显得过于臃肿
所以遇到这种异步请求或者非常复杂的逻辑,最好是把它移出到其他页面进行统一的处理,
这个时候 Redux-thunk
这个中间件就显得至关重要了,它可以将这些异步请求或者是复杂的逻辑放到 action 去处理,那如何使用 Redux-thunk
这个中间件呢?
打开github,搜索 Redux-thunk ,star最多的项目,就是Redux-thunk
按照它的使用说明进行如下操作
import { createStore, applyMiddleware } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk' const store = createStore(
reducer,
applyMiddleware(thunk) // applyMiddleware可以使用中间件模块
) export default store
需要注意的是:
中间件是通过创建 redux 的 store 时使用的,所以这个中间件是指的 redux 中间件,而不是 react 中间件
原则上 action 返回的是一个对象,但当我们使用
redux-thunk
中间件后, action 就可以返回一个函数了,继而可以在函数里边进行异步操作,也就可以把 TodoList 获取数据的请求放入这个函数中了
接着操作,在 actionCreator 中创建 action 的函数,然后数据传给 store
那问题来了,怎么传呢?本质还是调用 dipatch 方法,但是现在 actionCreactor 这个文件里并没有 store 这个数据仓库,也就没有 dispatch 这个方法,怎么办呢?
实际上,当我们创建一个内容是函数的 action 时,返回的函数就会自动接收到 store.dispatch
这个方法,所以只要在返回的函数里调用 dispatch ,然后派发 action 就好了, store 判断接收的 action 是一个对象,就会接收并发送给 reducer 进行数据更新操作
export const getTodoList = () => {
return (dispatch) => {
axios.get('/list.json').then((res) => {
const data = res.data
const action = initListAction(data)
dispatch(action)
})
}
}
在 TodoList 组件中引用这个创建内容是函数的 action
componentDidMount() {
const action = getTodoList ();
store.dispatch(action);
}
有的小伙伴可能会有疑问,就一个ajax请求,放在 componentDidMount 会有影响吗?
考虑到后期代码量的增加,如果把异步函数放在组件的生命周期里,这个生命周期函数会变得越来越复杂,组件就会变得越来越大
所以,还是应该把这种复杂的业务逻辑或者异步函数拆分到一个地方进行管理,现在借助 redux-thunk
,就可以放在 actionCreactor 里边集中管理,除此之外,在做自动化测试的时候,测试 actionCreactor 这个方法,也会比测组件的生命周期函数要简单的多
到底什么是 Redux 中间件
view 到 redux 的过程中会派发一个 action , action 通过 Store 的 dispatch 方法,会派发给 store , store接收到 action ,再连同之前的 state 一起传给 reducer , reducer 返回一个新的数据给 store , store 就可以去改变自己的 state ,组件接收到新的 state 就可以重新渲染页面了
redux的中间件在这个流程里边,指的是谁和谁之间呢?指的是 action 和 store 中间
action 通过 dispatch 方法被传递给 store ,那么 action 和 store 之间是不是就是 dispatch 这个方法呢?实际上,我们说的中间件就是指的 dispatch 方法的一个封装,或者是对 dispatch 方法的一个升级
最原始的 dispatch 方法,接收到对象 action 后会传递给 store ,这就是没有中间件的情况
对 dispatch 方法做了一个升级后,也就是使用中间件时,再调用 dispatch 方法,如何给 dispatch 传递的仍然是个对象, dispatch 就会把这个对象传给 store ,跟之前的方法没有任何区别;但是假如传的是个函数,就不会直接传递给 store 了,会让这个函数先执行,然后执行完之后需要调用 store ,这个函数再去调用 store
dispatch方法会根据参数的不同,执行不同的事情,如果参数是对象,就直接传给store,如果是函数,那就把函数执行结束
所以,redux的中间件原理很简单,就是对 store 的 dispatch 方法做一个升级,既可以接收对象,又可以接收函数了,那是用什么方法进行的升级的呢?就是用 redux-thunk
这个中间件进行升级的
当然,redux的中间件还有 redux-log
,原理就是在派发 action 给 store 之前先 console.log 出来;还有 redux-saga
,接下来需要讲解的
React-Redux 的使用
目前我们已经了解了 react 和 redux ,那 React-Redux
是什么呢?它是一个第三方的模块,可以在 react 中非常方便是使用 redux
重新来编写 todolist 功能,在 index 文件中引入 react-redux
import React from 'react'
import ReactDOM from 'react-dom'
import TodoList from './TodoList'
import { Provider } from 'react-redux'
import store from './store' const App = (
<Provider store={store}>
<TodoList />
</Provider>
) ReactDOM.render(App, document.getElementById('root'))
Provider
实质是一个组件,是一个提供器,是 react-redux
的一个核心API,连接着 store , Provider
里边所有的组件,都有能力获取到 store 里边的内容
react-redux
的另一个核心方法叫做 connect ,接收三个参数,最后一个参数是连接的组件,前面两个是连接的规则
之前说 Provider 组件连接了 store , Provider 内部的组件有能力获取到 store ,是怎样获取的呢?就是通过 connect 这个方法获取到里面的数据的
意思是让 TodoList 组件和 store 进行连接,所以 connect 方法的意思是做连接,在做连接时需要有一定的方式和规则,就是用 mapStateToProps
方法来做关联,翻译为中文就是把 store 里的数据 inputValue 映射到组件 inputValue 这个位置,为组件的 props 的数据
import React, { Component } from 'react'
import { connect } from 'react-redux' class TodoList extends Component {
render () {
return (
<div>
<div>
<input value={this.props.inputValue} />
<button>提交</button>
</div>
<ul>
<li>Dell</li>
</ul>
</div>
)
}
} const mapStateToProps = (state) => {
return {
inputValue: state.inputValue, }
} export default connect(mapStateToProps, null)(TodoList)
如果需要对 store 的数据做修改,dispatch 是指的 store.dispatch
,可以通过 mapDispatchToProps
方法把 store.dispatch
挂载到props上,为什么呢?
因为想要改变 store 里的内容,就要调用 dispatch 方法, dispatch 方法被映射到了 props 上,所以就可以通过 this.props.dispatch 方法去调用了
import React, { Component } from 'react'
import { connect } from 'react-redux' class TodoList extends Component {
render () {
return (
<div>
<div>
<input value={this.props.inputValue} onChange={this.props.handleInputChange} />
<button>提交</button>
</div>
<ul>
<li>Dell</li>
</ul>
</div>
)
}
} const mapStateToProps = (state) => {
return {
inputValue: state.inputValue
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
dispatch(action)
}
}
} export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
现在在 input 里输入值的功能就完成了,那todolist的增加功能怎么实现呢?
(TodoList.js)
<button onClick={this.props.handleClick}>提交</button> const mapDispatchToProps = (dispatch) => {
return {
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
dispatch(action)
}, handleClick() {
const action = {
type: 'add_todo_item'
}
dispatch(action)
}
}
}
(reducer.js)
export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
return state
}
点击这个 button 的时候,会执行 handleClick 这个方法,这个方法会把创建出来的 action 传给 store ,再传给 reducer, reducer 接收到这个 action 之后,去处理数据,把新的数据返回出去,新的数据就包含列表项的新内容了,数据发生了改变,todolist 组件恰好又通过 connect 跟数据做了连接,所以这块是个自动的流程,数据一旦发生改变,这个组件自动就会跟的变
以前还需要 store.subscribe
做订阅,现在连订阅都可以不用了,页面自动跟随数据发生变化
这样写就实现了增加 item 的功能,
比如 item 的删除操作, action 要通过 actionCreator
来创建,同时,还需要把 action 的 type 字符串放在 actionType
里面进行管理等等
创建 TodoList 这个组件,正常来说都是 export default TodoList
,把这个组件导出出去,但是�现在 export defalut 出的东西是通过 connect 方法执行的结果,connect 方法做了一件什么事呢?
它把这些映射关系和业务逻辑集成到了 TodoList 这个 UI 组件之中,所以 connect 方法可以这样理解,TodoList 是一个 UI 组件,当你用 connect 把这个 UI 组件和一些数据和逻辑相结合时,返回的内容实际就是一个容器组件了,容器组件可以理解成数据处理包括派发这样的业务逻辑,对 UI 组件进行包装,去调用这些UI组件,数据和方法都准备好了
有的小伙伴可能在网上看到过这样的描述,react-redux
组件既有 UI 组件,又有容器组件。UI 组件就是 TodoList 这个东西,而容器组件就是 connect 方法返回的结果,或者说 connect 方法执行生成的内容
所以 export default
导出的内容就是 connect
方法执行的结果,是一个容器组件