Redux 是什么
Redux is a predictable state container for JavaScript apps
Redux概念
store: 应用数据的存储中心
action: 应用数据的改变的描述
reducer: 决定应用数据新状态的函数,接收应用之前的状态和一个 action 返回数据的新状态。
state: 状态
middleware: redux 提供中间件的方式,完成一些 流程的自定义控制,同时形成其插件体系。
流程
环境准备
npx create-react-app cra
cd cra
npm start
在 cra项目中安装redux
yarn add redux
redux整体感知
// reducer
const weight = (state = 160, action) => {
switch (action.type) {
case 'eat':
return state + 10
case 'hungry':
return state - 10
default:
return 160
}
}
const store = createStore(weight)
console.log(store.getState())
store.dispatch({ type: 'eat' })
console.log('我吃了一些事物')
console.log(store.getState())
console.log('我饿了好几天')
store.dispatch({ type: 'hungry' })
console.log(store.getState())
console.log('我又饿了好几天')
store.dispatch({ type: 'hungry' })
console.log(store.getState())
reducer 里面使用switch语句根据传入的类型,输出新的状态
把reducer 传入 createStore(weight)
通过 dispatch 传入不同的类型,改变状态state。
store.dispatch({ type: 'hungry' })
通过 store.getState() 获取当前的状态
通过subscribe订阅,实现redux数据响应
const store = createStore(weight)
const watch = () => {
const cur = store.getState()
console.log(`stark wang 现在体重为${cur}斤`)
}
store.subscribe(watch)
// watch()
store.dispatch({type:'eat'})
store.dispatch({type:'eat'})
store.dispatch({type:'eat'})
store.dispatch({ type: 'hungry' })
redux和react的使用
stark.redux.js
import { createStore } from 'redux'
const EAT = 'eat'
const HUNGRY = 'hungry'
// reducer
export const weight = (state = 160 , action) => {
switch (action.type) {
case EAT:
return state + 10
case HUNGRY:
return state - 10
default:
return 160
}
}
// action
export const eat = () => {
console.log('stark wang 正在吃山珍海味')
return {type: EAT}
}
export const hungry = () => {
console.log('饿了一天')
return {type: HUNGRY}
}
App.js
import React from 'react'
import logo from './logo.svg'
import './App.css'
import { eat, hungry } from './stark.redux'
class App extends React.Component {
render () {
const store = this.props.store
const num = store.getState()
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<p>
【redux完全指南】系列1:从入门到会用
<br></br>
hi gusy, I am stark
</p>
<a
className='App-link'
href='https://shudong.wang'
target='_blank'
rel='noopener noreferrer'>跟着stark wang 学redux</a>
<h2>stark wang 当前的体重为{num}斤</h2>
<button onClick={() => {store.dispatch(eat())}}>
吃了一坨山珍海味
</button>
<button onClick={() => {store.dispatch(hungry())}}>
饿了一天
</button>
</header>
</div>
)
}
}
export default App
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux'
import { weight } from './stark.redux'
const store = createStore(weight)
const render = () =>{
ReactDOM.render(<App store={store} />, document.getElementById('root'));
}
render()
store.subscribe(render)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
使用connect让react和redux更优雅的结合
import React from 'react'
import logo from './logo.svg'
import './App.css'
import { eat, hungry } from './stark.redux'
import { connect } from 'react-redux'
class App extends React.Component {
render () {
const {num, eat, hungry} = this.props
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<p>
【redux完全指南】系列1:从入门到会用
<br></br>
hi gusy, I am stark
</p>
<a
className='App-link'
href='https://shudong.wang'
target='_blank'
rel='noopener noreferrer'>跟着stark wang 学redux</a>
<h2>stark wang 当前的体重为{num}斤</h2>
<button onClick={() => {eat()}}>
吃了一坨山珍海味
</button>
<button onClick={() => {hungry()}}>
饿了一天
</button>
</header>
</div>
)
}
}
// 把state挂在到 props 上面
// const mapStateToProps = (state)=>{
// return {num:state}
// }
// 把action 挂在到 props上面
// const actionCreators = {eat,hungry}
export default connect((state)=>{
return {num:state}
},{eat,hungry})(App)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux'
import { weight } from './stark.redux'
import { Provider } from 'react-redux'
const store = createStore(weight)
// const render = () =>{
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// }
// render()
// store.subscribe(render)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
使用react-thunk解决异步action问题
使用方式
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
const store = createStore(weight, applyMiddleware(thunk))
export const eatAsync = () =>{
return dispatch =>{
setTimeout(()=>{
// dispatch(eat('煎饼'))
dispatch({type: EAT})
},2000)
}
}
源码
import { createStore } from 'redux'
const EAT = 'eat'
const HUNGRY = 'hungry'
// reducer
export const weight = (state = 160 , action) => {
switch (action.type) {
case EAT:
return state + 10
case HUNGRY:
return state - 10
default:
return 160
}
}
// const store = createStore(weight)
// action
export const eat = (data) => {
console.log('stark wang 正在吃'+ data)
return {type: EAT}
}
export const hungry = () => {
console.log('饿了一天')
return {type: HUNGRY}
}
export const eatAsync = () =>{
return dispatch =>{
setTimeout(()=>{
dispatch(eat('煎饼'))
},2000)
}
}
import React from 'react'
import logo from './logo.svg'
import './App.css'
import { eat, hungry, eatAsync } from './stark.redux'
import { connect } from 'react-redux'
class App extends React.Component {
render () {
const {num, eat, hungry , eatAsync} = this.props
return (
<div className='App'>
<header className='App-header'>
<img src={logo} className='App-logo' alt='logo' />
<p>
【redux完全指南】系列1:从入门到会用
<br></br>
hi gusy, I am stark
</p>
<a
className='App-link'
href='https://shudong.wang'
target='_blank'
rel='noopener noreferrer'>跟着stark wang 学redux</a>
<h2>stark wang 当前的体重为{num}斤</h2>
<button onClick={() => {eat()}}>
吃了一坨山珍海味
</button>
<button onClick={() => {eatAsync()}}>
等了一会,吃了一个煎饼
</button>
<button onClick={() => {hungry()}}>
饿了一天
</button>
</header>
</div>
)
}
}
// const mapStateToProps = (state)=>{
// return {num:state}
// }
// const actionCreators = {eat,hungry}
export default connect((state)=>{
return {num:state}
},{eat,hungry,eatAsync})(App)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore, applyMiddleware } from 'redux'
import { weight } from './stark.redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
const store = createStore(weight, applyMiddleware(thunk))
// const render = () =>{
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// }
// render()
// store.subscribe(render)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
使用axios请求真实的api
export const getArticle = () =>{
return dispatch =>{
setTimeout(()=>{
// dispatch(eat('煎饼'))
dispatch(()=>{
axios.get('/v1/article/list').then(res=>{
console.log('article',res)
})
})
},2000)
}
}
解决跨域问题
1.
npm run eject
2.
在package.json 文件末尾添加 "proxy": "http://api.shudong.wang"
3.
axios.get('/v1/article/list').then(res=>{
console.log('article',res)
})
把请求api数据渲染到页面上面
拆分state把请求api文章列表,数据渲染到页面上
const initState = {
num: 160,
list: []
}
// reducer
export const weight = (state = initState , action) => {
switch (action.type) {
case EAT:
return {...state, num: state.num + 10}
case HUNGRY:
return {...state, num: state.num - 10}
case ARTICLE_LIST:
console.log('action',action)
return {...state, list: action.playload.article}
default:
return initState
}
}
export const getArticle = () =>{
return dispatch =>{
setTimeout(()=>{
// dispatch(eat('煎饼'))
axios.get('/v1/article/list').then(res=>{
console.log('article',res)
//做了一个分发
dispatch({type:ARTICLE_LIST,playload:res.data})
})
},2000)
}
}
页面渲染
const {num, eat, hungry , eatAsync, getArticle, list} = this.props
render
{list.map((item,index)=>{
return <div key={index}>
{item.title}
</div>
})}
export default connect((state)=>{
return {num: state.num,list:state.list}
},{eat,hungry,eatAsync,getArticle})(App)
装饰器模式
babel
npm i @babel/plugin-proposal-decorators -S
"babel": {
"presets": [
"react-app"
],
"plugins":[
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
},
语法糖{把state 和 action 挂在到 当前的组件props上}
第一个参数是 state 第二个参数是action
// const actionCreators = {eat,hungry}
// export default connect((state)=>{
// return {num: state.num,list:state.list}
// },{eat,hungry,eatAsync,getArticle})(App)
@connect(state => state, { eat, hungry, eatAsync, getArticle })
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。