朋也的博客 » 首页 » 文章
作者:朋也
日期:2021-04-29
类别:react.js学习笔记
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
本文内容整理自尚硅谷视频
安装
yarn add redux
创建store
import { createStore } from 'redux'
import countReducer from './count_reducer'
export default createStore(countReducer)
创建reducer
const initState = 0
export default function countReducer(preState = initState, action) {
console.log(preState, action)
const { type, data } = action;
switch (type) {
case "increment":
return preState + data;
case "decrement":
return preState - data;
default:
return preState;
}
}
编写组件
import React from 'react';
import store from './redux/store'
class App extends React.Component {
increment() {
store.dispatch({ type: "increment", data: 1 })
}
decrement() {
store.dispatch({ type: "decrement", data: 1 })
}
componentDidMount() {
// 因为redux只管理state,不负责渲染,所以这里通用调用subscribe()方法监听一下store里状态值的变化,然后通过调用setState()方法什么值都不改来达到更新组件的目的
store.subscribe(() => {
this.setState({})
})
}
render() {
return (
<div className="App">
<code>Count: {store.getState()}</code><br />
<button onClick={this.increment.bind(this)}>increment</button>
<button onClick={this.decrement.bind(this)}>decrement</button>
</div >
);
}
}
export default App;
上面案例中用到了redux里三个api
添加上constant.js用于定义一些action中type的常量
export const INCREMENT = "increment"
export const DECREMENT = "decrement"
添加上count_action.js用于定义事件对象
import { INCREMENT, DECREMENT } from './constant'
export const countIncrement = data => ({ type: INCREMENT, data });
export const countDecrement = data => ({ type: DECREMENT, data });
组件中使用方法
import { countIncrement, countDecrement } from './redux/count_action'
class App extends React.Component {
increment() {
store.dispatch(countIncrement(1))
}
decrement() {
store.dispatch(countDecrement(1))
}
//...
}
当action返回的是一个对象(Object)时,它就是一个同步的action,但如果涉及到网络请求时,就属于异步操作了,同时action也要是异步的 redux规定,action返回对象是一个function时,它就是一个异步的action,写法如下
import { INCREMENT, DECREMENT } from './constant'
import store from './store'
export const countIncrement = data => ({ type: INCREMENT, data });
export const countDecrement = data => ({ type: DECREMENT, data });
// 异步的action
export const countIncrementAsync = (data, time) => {
return () => {
setTimeout(() => {
store.dispatch(countIncrement(data))
}, time);
}
}
调用
class App extends React.Component {
increment() {
store.dispatch(countIncrementAsync(1, 1000))
}
//...
}
这时候运行会报错,还要借助一个中间件 redux-thunk 才能让redux支持异步action,安装 yarn add redux-thunk 将中间件配置在store.js里,写法是固定的
import { createStore, applyMiddleware } from 'redux'
import countReducer from './count_reducer'
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))
这时候再运行就没问题了。
react-redux又将组件分为UI组件与容器组件(概念是真多。。)
首先要安装依赖 yarn add react-redux
然后将App.js里所有的redux相关的代码都删了,然后创建一个Count容器组件,容器组件一般放在container目录里,名字也叫App.js
import { connect } from 'react-redux'
// 引入UI组件
import AppUI from '../components/App'
// 引入actions
import { countIncrement, countDecrement, countIncrementAsync } from '../redux/count_action'
// connect()方法的第一个参数,它是将redux里的state映射到ui组件的props,然后传给ui组件使用
function mapStateToProps(state) {
return { abc: state }
}
// connect()方法的第二个参数,它是将redux里的dispatch映射到ui组件的props,然后传给ui组件使用
function mapDispatchToProps(dispatch) {
return {
jia: (data) => dispatch(countIncrement(data)),
jian: (data) => dispatch(countDecrement(data)),
jiaAsync: (data, time) => dispatch(countIncrementAsync(data, time))
}
}
// connect()() 方法的返回值就是ui组件
export default connect(mapStateToProps, mapDispatchToProps)(AppUI)
然后修改index.js里引入的App组件,现在要引入的是容器组件,然后将store传给这个容器组件
import React from 'react';
import ReactDOM from 'react-dom';
// 引入容器组件,在这个容器组件里关联了UI组件
import App from './containers/App';
import store from './redux/store'
ReactDOM.render(
<React.StrictMode>
<App store={store} /> // 将store传给容器组件使用
</React.StrictMode>,
document.getElementById('root')
);
真正的UI组件里使用redux里的state与dispatch方法就要从props里取了
import React from 'react';
class App extends React.Component {
increment() {
this.props.jia(1);
}
decrement() {
this.props.jian(1);
}
incrementAsync() {
this.props.jiaAsync(1, 1000);
}
render() {
return (
<div className="App">
{/* <code>Count: {store.getState()}</code><br /> */}
<code>Count: {this.props.abc}</code><br />
<button onClick={this.increment.bind(this)}>increment</button>
<button onClick={this.decrement.bind(this)}>decrement</button>
<button onClick={this.incrementAsync.bind(this)}>increment async</button>
</div >
);
}
}
export default App;
首先在容器组件被使用的地方要把store传进去,那么当容器组件不止一个的时候,有多少组件就发写多少次store={store},如下
return (
<div>
<App store={store}/>
<App1 store={store}/>
<App2 store={store}/>
<App3 store={store}/>
</div>
)
这样写起来就很不优雅了,react-redux里提供了一个Provider组件可以将配置在Provider上的store自动的注册到它下面所有的子组件上去,所以这时候就可以在项目的最外层的组件外给包上一层Provider组件,然后将store给传进去就行了
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import App from './containers/App';
import store from './redux/store'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
容器组件里的connect()方法里的两个参数可以优化成下面样子
import { connect } from 'react-redux'
// 引入UI组件
import AppUI from '../components/App'
// 引入actions
import { countIncrement, countDecrement, countIncrementAsync } from '../redux/count_action'
// connect()() 方法的返回值就是ui组件
export default connect(
state => ({ abc: state }),
({
jia: countIncrement,
jian: countDecrement,
jiaAsync: countIncrementAsync,
})
)(AppUI)
一个组件用上react-redux后,还要拆成两个组件(ui组件,容器组件)就太麻烦了,这两个组件也是可以合并的
import React from 'react';
import { connect } from 'react-redux'
// 引入actions
import { countIncrement, countDecrement, countIncrementAsync } from '../redux/count_action'
function App(props) {
console.log(props);
function increment() {
props.jia(1)
}
function decrement() {
props.jian(1)
}
function incrementAsync() {
props.jiaAsync(1, 1000)
}
return (
<div className="App">
<code>Count: {props.abc}</code><br />
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<button onClick={incrementAsync}>increment async</button>
</div >
);
}
// connect()() 方法的返回值就是ui组件
export default connect(
state => ({ abc: state }),
({
jia: countIncrement,
jian: countDecrement,
jiaAsync: countIncrementAsync,
})
)(App)
甚至于如果将mapDispatchToProps中的方法名写成跟action中一样的话,可以简写如下
export default connect(
state => ({ abc: state }),
({
countIncrement,
countDecrement,
countIncrementAsync,
})
)(App)
继续简化,如果connect()方法的第二个参数不传,那么react-redux会默认将dispatch方法添加到props上,调用时就要用 props.dispatch(xxx()) 来写了
function App(props) {
console.log(props);
function increment() {
props.dispatch(countIncrement(1))
}
function decrement() {
props.dispatch(countDecrement(1))
}
function incrementAsync() {
props.dispatch(countIncrementAsync(1, 1000))
}
return (
<div className="App">
<code>Count: {props.abc}</code><br />
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<button onClick={incrementAsync}>increment async</button>
</div>
);
}
export default connect(
state => ({ abc: state })
)(App)
既然第二个参数能省,那么第一个参数state能不能省呢?不行
当有多个组件时,每个组件都对应的有一个reducer,一个action的时候,这时候就要分文件夹管理了,当有多个reducer的时候,可以借助redux里的combineReducers()方法把这些reducer给集中管理起来,然后再一次性的传给createStore()方法
import { createStore, applyMiddleware, combineReducers } from 'redux'
import countReducer from './reducers/count'
import personReducer from './reducers/person'
import thunk from 'redux-thunk'
const allReducers = combineReducers({
count: countReducer,
persons: personReducer
})
export default createStore(allReducers, applyMiddleware(thunk))
当有多个组件时,在两个不相干的组件中使用对方的状态数据时,可以在connect()方法里声明,比如在Author组件里还想展示当前作者写的书的数据,那么就要用到Books组件里的数据了,可以通过下面方法拿到对方的状态数据
export default connect(state => ({ author: state.author, books: state.books }))(Author);
react开发有对应的插件,redux也有,不过不像react插件那么方便,redux要在代码里配置一下才能在浏览器里让插件生效 首先安装,地址: Redux DevTools - Chrome 网上应用店 (google.com)
然后安装依赖 yarn add redux-devtools-extension
最后修改store.js里的createStore()代码
import { createStore, applyMiddleware, combineReducers } from 'redux'
import countReducer from './reducers/count'
import personReducer from './reducers/person'
import thunk from 'redux-thunk'
// 引入工具依赖
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
const allReducers = combineReducers({
count: countReducer,
persons: personReducer
})
// 放在中间件的参数位置,如果有其它中间件,就当参数传给composeWithDevTools()
export default createStore(allReducers, composeWithDevTools(applyMiddleware(thunk)))