React + Redux
在 recat 中不应用 redux 时遇到的问题
在 react 中组件通信的数据是单向的,顶层组件能够通过 props 属性向上层组件传递数据,而上层组件不能向下层组件传递数据,要实现上层组件批改数据,须要下层组传递批改数据的办法到上层组件,当我的项目越来越的时候,组件之间传递数据变得越来越艰难
在 react 中退出 redux 的益处
应用 redux 治理数据,因为 Store 独立于组件,使得数据管理独立于组件,解决了组件之间传递数据艰难的问题
应用 redux
下载 redux
npm install redux react-redux
redux 工作流程
- 组件通过 dispatch 触发 action
- store 承受 action 并将 action 分发给 reducer
- reducer 依据 action 类型对状态进行更改并将更改后的数据返回给 store
- 组件订阅了 store 中的状态,store 中的状态更新会同步到组件
应用 react+redux 实现计数器
- 创立我的项目,并装置 redux
# 如果没有装置 react 脚手架则执行这条命令装置 reate 脚手架
npm install -g create-react-app
# 创立 reate 我的项目
create-react-app 我的项目名
# 进入我的项目
cd 我的项目名
# 装置 redux
npm install redux reate-redux
- 引入 redux,并依据开始实现的代码在 react 中实现计数器
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {createStore} from 'redux';
const initialState = {count: 0}
function reducer(state = initialState, action) {switch (action.type) {
case 'increment':
return {count: state.count + 1}
case 'decrement':
return {count: state.count - 1}
default:
return state
}
}
const store = createStore(reducer)
const increment = {type: 'increment'}
const decrement = {type: 'decrement'}
function Count() {
return <div>
<button onClick={() => store.dispatch(increment)}>+</button>
<span>{store.getState().count}</span>
<button onClick={() => store.dispatch(decrement)}>-</button>
</div>
}
store.subscribe(() => {console.log(store.getState())
ReactDOM.render(
<React.StrictMode>
<Count />
</React.StrictMode>,
document.getElementById('root')
);
})
ReactDOM.render(
<React.StrictMode>
<Count />
</React.StrictMode>,
document.getElementById('root')
);
显著以上形式尽管能够实现计数器的性能,但在理论我的项目中必定不能这样应用,因为组件个别都在独自的文件中的,这种形式显著在其余组件中并不能获取到 Store。
计数器案例代码优化 - 让 store 全局可拜访
为了解决 Store 获取问题须要应用 react-redux 来解决这个问题,react-redux 给咱们提供了 Provider 组件和 connect 办法
- Provide 组件
是一个组件 能够吧创立进去的 store 放在一个全局的中央, 让组件能够拿到 store, 通过 provider 组件, 将 store 放在了全局的组件能够够的到的中央 ,provider 要求咱们放在最外层组件
- connect
connect 帮忙咱们订阅 store 中的状态,状态产生扭转后帮忙咱们从新渲染组件
通过 connect 办法咱们能够拿到 store 中的状态 把 store 中的状态映射到 props 中
通过 connect 办法能够拿到 dispatch 办法
connect 的参数为一个函数 这个函数能够拿到 store 中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的 props 属性
connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知 connect 须要映射到那个组件的 props
- 新建 Component 文件夹、创立 Count.js 文件
import React from 'react'
function Count() {
return <div>
<button onClick={() => store.dispatch(increment)}>+</button>
<span>{store.getState().count}</span>
<button onClick={() => store.dispatch(decrement)}>-</button>
</div>
}
export default Count
- 引入 Provider 组件搁置在最外层,并制订 store
ReactDOM.render(
// 通过 provider 组件 将 store 放在了全局的组件能够够的到的中央 provider 要求咱们放在最外层组件
<Provider store={store}><Count /></Provider>,
document.getElementById('root')
);
- 引入 connect 办法 依据 connect 的应用来包裹组件
const mapStateProps = state => ({
count: state.count,
a: '1'
})
// connect 的参数为一个函数 这个函数能够拿到 store 中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的 props 属性
// connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知 connect 须要映射到那个组件的 props
export default connect(mapStateProps)(Count)
- 革新 Count 组件把 action 复制到该文件中
const increment = {type: 'increment'}
const decrement = {type: 'decrement'}
function Count({count,dispatch}) {
return <div>
<button onClick={() => {dispatch(increment)}}>+</button>
<span>{count}</span>
<button onClick={() => {dispatch(decrement)}}>-</button>
</div>
}
当初我的项目曾经能够运行了然而 Count 组件中的 提交 Action 的那一长串代码影响视图的可读性,所以代码还是须要优化
计数器案例代码优化 - 让视图中的代码可读性更高
咱们心愿视图中间接调用一个函数这样视图代码可读性强,这个须要利用 connect 的第二个参数,第二个参数是一个函数,这个函数的形参就是 dispatch 办法,要求这个函数返回一个对象,返回的这个对象中的内容都会映射到组件的 props 属性上
- 申明一个变量为 connect 中的第二个参数,在这个变量中返回执行不同 action 操作的对象
// connect 的第二个参数 这个参数是个函数 这个函数的形参就是 dispatch 办法 要求返回一个对象 这个对象中的属性会被映射到组件的 props 上
const mapDispatchToProps = dispatch => ({increment (){
dispatch({type: 'increment'})
},
decrement (){
dispatch({type: 'decrement'})
}
})
// connect 的参数为一个函数 这个函数能够拿到 store 中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的 props 属性
// connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知 connect 须要映射到那个组件的 props
export default connect(mapStateProps, mapDispatchToProps)(Count)
- 在组件中构造 props 在视图中间接绑定事件
function Count({count,increment,decrement}) {
return <div>
<button onClick={increment}>+</button>
<span>{count}</span>
<button onClick={decrement}>-</button>
</div>
}
通过这次优化咱们发现 调用 dispatch 触发 action 的办法的代码都是反复的,所以还须要持续优化
优化调用 dispatch 触发 action 的办法的反复代码简化
利用 bindActionCreators 来简化 dispatch 触发 action 的操作,bindActionCreators 来帮忙咱们生成执行 action 动作的函数
bindActionCreators 有两个参数,第一个参数为 执行 action 的对象,第二个参数为 dispatch 办法
- 拆散 action 操作,新建 store/actions/counter.actions.js 文件把执行 action 操作独自放在这个文件并导出
export const increment = () => ({type: 'increment'})
export const decrement = () => ({type: 'decrement'})
- 在 Count.js 中导入对于计数器的 action,用 bindActionCreators 办法来生成 dispatch 执行 action 函数
import {bindActionCreators} from 'redux'
import * as counterActions from './../store/actions/counter.actions'
const mapDispatchToProps = dispatch => (bindActionCreators(counterActions, dispatch))
// connect 的参数为一个函数 这个函数能够拿到 store 中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的 props 属性
// connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知 connect 须要映射到那个组件的 props
export default connect(mapStateProps, mapDispatchToProps)(Count)
代码优化到这里咱们发现,redux 的代码与组件交融在一起,所以我须要拆分成独立的,为什么要抽离 redux 呢?因为咱们要让咱们的代码构造更加正当
重构计数器,把 redux 相干代码抽离
把 reducer 函数抽离为独自的文件、把创立 store 抽离到独自的文件中
- 因为在 reducer 和 actions 中咱们都写了字符串,然而字符串没有提醒所以咱们把字符串定义成常量避免咱们呈现单词谬误这种低级谬误,新建 src/store/const/counter.const.js 文件
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
- 新建 src/store/reducers/counter.reducers.js 文件把 reducer 函数抽离到此文件中
import {INCREMENT, DECREMENT} from './../const/counter.const'
const initialState = {count: 0}
// eslint-disable-next-line import/no-anonymous-default-export
export default (state = initialState, action) => {switch (action.type) {
case INCREMENT:
return {count: state.count + 1}
case DECREMENT:
return {count: state.count - 1}
default:
return state
}
}
- 更改 actions 中的字符串为引入变量
import {INCREMENT, DECREMENT} from './../const/counter.const'
export const increment = () => ({type: INCREMENT})
export const decrement = () => ({type: DECREMENT})
- 创立 src/store/index.js 文件,在这个文件中创立 store 并导出
import {createStore} from 'redux';
import reducer from './reducers/counter.reducers'
export const store = createStore(reducer)
- 在引入 store 的文件中扭转为冲我的项目中 store 文件中引入 store
import React from 'react';
import ReactDOM from 'react-dom';
import Count from './components/Count';
import {store} from './store'
import {Provider} from 'react-redux'
/**
* react-redux 让 react 和 redux 完满联合
* Provider 是一个组件 能够吧创立进去的 store 放在一个全局的中央 让组件能够拿到 store
* connect 是一个办法
*/
ReactDOM.render(
// 通过 provider 组件 将 store 放在了全局的组件能够够的到的中央 provider 要求咱们放在最外层组件
<Provider store={store}><Count /></Provider>,
document.getElementById('root')
);
为 action 传递参数, 对计数器案例做扩大
这个计数器案例曾经实现了点击按钮加一减一操作了,当初有个新需要咱们须要加减一个数值例如加五减五
这就须要对 action 传递参数了
- 在视图中按钮绑定函数传入参数
function Count({count,increment,decrement}) {
return <div>
<button onClick={() => increment(5)}>+</button>
<span>{count}</span>
<button onClick={() => decrement(5)}>-</button>
</div>
}
- 在 dispacth 执行 action 动作时承受参数并传入到 action 中
export const increment = payload => ({type: INCREMENT, payload})
export const decrement = payload => ({type: DECREMENT, payload})
- 在 reducers 中接管参数并作相应解决
export default (state = initialState, action) => {switch (action.type) {
case INCREMENT:
return {count: state.count + action.payload}
case DECREMENT:
return {count: state.count - action.payload}
default:
return state
}
}
原文地址:https://kspf.xyz/archives/10/