在react中组件通信的数据是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组传递修改数据的方法到下层组件,当项目越来越的时候,组件之间传递数据变得越来越困难
使用redux管理数据,由于Store独立于组件,使得数据管理独立于组件,解决了组件之间传递数据困难的问题
npm install redux react-redux
# 如果没有安装react脚手架则执行这条命令安装reate脚手架
npm install -g create-react-app
# 创建reate项目
create-react-app 项目名
# 进入项目
cd 项目名
# 安装 redux
npm install redux reate-redux
//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获取问题需要使用react-redux来解决这个问题,react-redux给我们提供了Provider组件和connect方法
是一个组件 可以吧创建出来的store 放在一个全局的地方,让组件可以拿到store,通过provider组件,将 store 放在了全局的组件可以够的到的地方 ,provider要求我们放在最外层组件
connect 帮助我们订阅store中的状态,状态发生改变后帮助我们重新渲染组件
通过 connect 方法我们可以拿到 store 中的状态 把 store 中的状态映射到props中
通过 connect 方法可以拿到 dispatch 方法
connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
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
ReactDOM.render(
// 通过provider组件 将 store 放在了全局的组件可以够的到的地方 provider要求我们放在最外层组件
<Provider store={store}><Count /></Provider>,
document.getElementById(‘root‘)
);
const mapStateProps = state => ({
count: state.count,
a: ‘1‘
})
// connect 的参数为一个函数 这个函数可以拿到store中的状态,要求我们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性
// connect 调用后返回一个函数 返回的这个函数继续调用需要传入组件告诉connect需要映射到那个组件的props
export default connect(mapStateProps)(Count)
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 的第二个参数 这个参数是个函数 这个函数的形参就是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)
function Count({count,increment,decrement}) {
return <div>
<button onClick={increment}>+</button>
<span>{count}</span>
<button onClick={decrement}>-</button>
</div>
}
通过这次优化我们发现 调用 dispatch 触发action 的方法的代码都是重复的,所以还需要继续优化
利用 bindActionCreators 来简化 dispatch 触发 action的操作,bindActionCreators来帮助我们生成执行action动作的函数
bindActionCreators 有两个参数,第一个参数为 执行action的对象,第二个参数为 dispatch方法
export const increment = () => ({type: ‘increment‘})
export const decrement = () => ({type: ‘decrement‘})
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呢?因为我们要让我们的代码结构更加合理
把reducer函数抽离为单独的文件、把创建store抽离到单独的文件中
export const INCREMENT = ‘increment‘
export const DECREMENT = ‘decrement‘
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
}
}
import { INCREMENT, DECREMENT} from ‘./../const/counter.const‘
export const increment = () => ({type: INCREMENT})
export const decrement = () => ({type: DECREMENT})
import { createStore } from ‘redux‘;
import reducer from ‘./reducers/counter.reducers‘
export const store = createStore(reducer)
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传递参数了
function Count({count,increment,decrement}) {
return <div>
<button onClick={() => increment(5)}>+</button>
<span>{count}</span>
<button onClick={() => decrement(5)}>-</button>
</div>
}
export const increment = payload => ({type: INCREMENT, payload})
export const decrement = payload => ({type: DECREMENT, payload})
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://www.cnblogs.com/fengbaba/p/14836768.html