一种管理状态的js工具。store数据仓库,数据和状态都放在这里,集中管理。
有四个部分。React Components
就相当于大官人,然后我们去作“大宝剑”,我们先见到的是Action Creators
“妈妈桑”,我们说我要找小红,我是熟客了。"妈妈桑"就回到了Store
,然后让Reducer
看看"小红“忙不忙(现在的状态),如果不忙就让她过来配大官人。
看一下例子,先看下组件:
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
import store from './store'
class TodoList extends Component {
constructor(props){
super(props)
//关键代码-----------start
this.state=store.getState();
//关键代码-----------end
console.log(this.state)
}
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input placeholder={this.state.inputValue} style={{ width:'250px', marginRight:'10px'}}/>
<Button type="primary">增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
//关键代码-----------start
dataSource={this.state.list}
//关键代码-----------end
renderItem={item=>(<List.Item>{item}</List.Item>)}
/>
</div>
</div>
);
}
}
export default TodoList;
store代码:
import { createStore } from 'redux' // 引入createStore方法
import reducer from './reducer'
const store = createStore(reducer) // 创建数据存储仓库
export default store //暴露出去
reducer:
const defaultState = {
inputValue : 'Write Something',
list:[
'早上4点起床,锻炼身体',
'中午下班游泳一小时'
]
}
export default (state = defaultState,action)=>{
return state
}
这里的两个参数
- state: 指的是原始仓库里的状态。
- action: 指的是action新传递的状态。
源代码创建action并dispatch上去:
changeInputValue(e){
const action ={
type:'changeInput',
value:e.target.value
}
store.dispatch(action)
}
在reducer里操作,写业务逻辑(Reducer里只能接收state,不能改变state)
export default (state = defaultState,action)=>{
if(action.type === 'changeInput'){
let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
newState.inputValue = action.value
return newState
}
return state
}
源代码在订阅下storechange()事件
constructor(props){
super(props)
this.state=store.getState();
this.changeInputValue= this.changeInputValue.bind(this)
//----------关键代码-----------start
this.storeChange = this.storeChange.bind(this) //转变this指向
store.subscribe(this.storeChange) //订阅Redux的状态
//----------关键代码-----------end
}
storeChange(){
this.setState(store.getState())
}
添加功能与删除功能,主要在reducer里的业务代码不同:
//添加
if(action.type === 'addItem' ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的内容到列表中去
newState.inputValue = ''
return newState
}
//关键代码------------------end----------
return state
//删除
if(action.type === 'deleteItem' ){
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1) //删除数组中对应的值
return newState
}
小技巧,Actiontype单独分出一个文件,一来type多的话在大型项目里难以管理,二来在用type的时候,在源文件和reducer文件里要一一对应,错的话很难调试。定义为常量另一个好处:写错了名称,控制台会报错,大大减少找错时间,
新建actionTypes.js
export const CHANGE_INPUT = 'changeInput'
export const ADD_ITEM = 'addItem'
export const DELETE_ITEM = 'deleteItem'
然后,源文件相应的名字都改为大写,替换原来的,像这样:
changeInputValue(e){
const action ={
type:CHANGE_INPUT,
value:e.target.value
}
store.dispatch(action)
}
clickBtn(){
const action = { type:ADD_ITEM }
store.dispatch(action)
}
deleteItem(index){
const action = { type:DELETE_ITEM, index}
store.dispatch(action)
}
对应的reducer.js文件也改为大写;
import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM} from './actionTypes'
const defaultState = {
inputValue : 'Write Something',
list:[
'早上4点起床,锻炼身体',
'中午下班游泳一小时'
]
}
export default (state = defaultState,action)=>{
if(action.type === CHANGE_INPUT){
let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
newState.inputValue = action.value
return newState
}
//state值只能传递,不能使用
if(action.type === ADD_ITEM ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的内容到列表中去
newState.inputValue = ''
return newState
}
if(action.type === DELETE_ITEM ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1) //push新的内容到列表中去
return newState
}
return state
}
三个小坑:
store
必须是唯一的,多个store
是坚决不允许,只能有一个store
空间- 只有
store
能改变自己的内容,Reducer
不能改变,深度拷贝验证了这一点 Reducer
必须是纯函数,返回的结果由传入的值决定
无状态组件:组件向静态化发展,这是趋势,性能会大大提高。
- 首先我们不在需要引入React中的
{ Component }
,删除就好。 - 然后些一个
TodoListUI
函数,里边只返回JSX
的部分就好,这步可以复制。 - 函数传递一个
props
参数,之后修改里边的所有props
,去掉this
。
import React from 'react';
import 'antd/dist/antd.css'
import { Input , Button , List } from 'antd'
const TodoListUi = (props)=>{
return(
<div style={{margin:'10px'}}>
<div>
<Input
style={{ width:'250px', marginRight:'10px'}}
onChange={props.changeInputValue}
value={props.inputValue}
/>
<Button
type="primary"
onClick={props.clickBtn}
>增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={props.list}
renderItem={
(item,index)=>(
<List.Item onClick={()=>{props.deleteItem(index)}}>
{item}
</List.Item>
)
}
/>
</div>
</div>
)
}
export default TodoListUi;
actionCreators():每个动作在这里写,写成箭头函数
和axios的结合:在组件挂载后的生命周期函数里写入axios
componentDidMount(){
axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{
const data = res.data
const action = getListAction(data)
store.dispatch(action)
})
}
reducer.js:
//----关键代码--------start --------引入GET_LIST
import {CHANGE_INPUT,ADD_ITEM,DELETE_ITEM,GET_LIST} from './actionTypes'
//----关键代码--------end
const defaultState = {
inputValue : 'Write Something',
//----关键代码--------start --------删除原来的初始化代码,减少冗余
list:[]
}
export default (state = defaultState,action)=>{
if(action.type === CHANGE_INPUT){
let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
newState.inputValue = action.value
return newState
}
if(action.type === ADD_ITEM ){
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的内容到列表中去
newState.inputValue = ''
return newState
}
if(action.type === DELETE_ITEM ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1) //push新的内容到列表中去
return newState
}
//----关键代码--------start --------
if(action.type === GET_LIST ){ //根据type值,编写业务逻辑
let newState = JSON.parse(JSON.stringify(state))
newState.list = action.data.data.list //复制性的List数组进去
return newState
}
//----关键代码--------en'd --------
return state
}
redux-thunk:一种中间件,用它来增强dispatch。
import { createStore , applyMiddleware ,compose } from 'redux' // 引入createStore方法
import reducer from './reducer'
import thunk from 'redux-thunk'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose
const enhancer = composeEnhancers(applyMiddleware(thunk))
const store = createStore( reducer, enhancer) // 创建数据存储仓库
export default store //暴露出去
用个增强器把thunk这个中间件加进去,当做第二个参数放进去。
有了这个中间件之后,可以把componentDidMount()的业务逻辑写在这里。
redux-saga:类似上面的内容,相当于百事可乐和可口可乐的关系,这里不展开,先用会一种再说。菜鸡心态哈哈。
react-redux和react,redux,这三者是不一样的,react-redux是react生态中的常用组件。写一个todolist.js,在构造函数里调用store
import React, { Component } from 'react';
//-----关键代码--------start
import store from './store'
//-----关键代码--------end
class TodoList extends Component {
//-----关键代码--------start
constructor(props){
super(props)
this.state = store.getState()
}
//-----关键代码--------end
render() {
return (
<div>
<div>
//-----关键代码--------start
<input value={this.state.inputValue} />
//-----关键代码--------end
<button>提交</button>
</div>
<ul>
<li>JSPang</li>
</ul>
</div>
);
}
}
export default TodoList;
react-redux中的Provider和connect,提供器和连接器。provider组件里的组件都可以使用store,即用provider组件包裹。connect连接器组件暴露出去就可以使用,如下:xxx代表一个映射关系。connect
的作用是把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再链接到一起,让代码更加清晰和易于维护。这也是React-Redux
最大的有点。
export default connect(xxx,null)(TodoList);
而映射关系怎么做出来,就是把原来的state映射为props属性,
const stateToProps = (state)=>{
return {
inputValue : state.inputValue
}
}
把xxx改为stateToProps:
export default connect(stateToProps,null)(TodoList)
这时,put里面的state就可以改为props:
<input value={this.props.inputValue} />
这里把全部的代码展示出来:
import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'
class TodoList extends Component {
constructor(props){
super(props)
this.state = store.getState()
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue} />
<button>提交</button>
</div>
<ul>
<li>JSPang</li>
</ul>
</div>
);
}
}
const stateToProps = (state)=>{
return {
inputValue : state.inputValue
}
}
export default connect(stateToProps,null)(TodoList);
编写DispatchToProps,这个函数是连接器的第二个参数,作用:把原来 this.inputChange.bind(this)这种双向绑定函数的方法,改为this.props方式调用方法。全部代码如下展示:
import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'
class TodoList extends Component {
constructor(props){
super(props)
this.state = store.getState()
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue} onChange={this.props.inputChange} />
<button>提交</button>
</div>
<ul>
<li>JSPang</li>
</ul>
</div>
);
}
}
const stateToProps = (state)=>{
return {
inputValue : state.inputValue
}
}
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
console.log(e.target.value)
}
}
}
export default connect(stateToProps,dispatchToProps)(TodoList);
映射关系做好后,还有action的派发和reducer的业务逻辑要处理:
派发action:
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
}
}
}
reducer的业务逻辑:
const defalutState = {
inputValue : 'jspang',
list :[]
}
export default (state = defalutState,action) =>{
if(action.type === 'change_input'){
let newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
return state
}
给按钮添加事件:
1,在按钮的属性里先写方法名
<button onClick={this.props.clickButton}>提交</button>
2,在dispatchtoprops里派发action,控制台打印出111111,然后派发action
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
},
clickButton(){
console.log('111111111')
let action = { type:'add_item' }
dispatch(action)
}
}
}
3.编写reducer的业务逻辑,点击即可增加一个列表。
const defalutState = {
inputValue : 'jspang',
list :[]
}
export default (state = defalutState,action) =>{
if(action.type === 'change_input'){
let newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
//----关键代码------start---------
if(action.type === 'add_item'){
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
//----关键代码------end---------
return state
}
4,在把statetoprops这个映射关系完善
const stateToProps = (state)=>{
return {
inputValue : state.inputValue,
list:state.list
}
}
5,有了这个关系,在界面中用属性的方式调用
<ul>
{
this.props.list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul
这样就实现了添加列表的功能,这里展示出全部代码;
import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'
class TodoList extends Component {
constructor(props){
super(props)
this.state = store.getState()
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue} onChange={this.props.inputChange} />
<button onClick={this.props.clickButton}>提交</button>
</div>
<ul>
{
this.props.list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}
}
const stateToProps = (state)=>{
return {
inputValue : state.inputValue,
list:state.list
}
}
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
},
clickButton(){
let action = {
type:'add_item'
}
dispatch(action)
}
}
}
export default connect(stateToProps,dispatchToProps)(TodoList);
代码精简:
https://juejin.im/post/5e169204e51d454112714580#heading-19,这是一篇关于HOC高阶组件的文章,其中的思想也有部分精简代码的,可以学习,高级组件不是组件,而是函数,react-redux里的connect连接器就是一个高阶组件,hook可以看做对高阶组件的补充。
1,这几处的this.props都是重复的,用js的解构赋值的方式精简一下代码:
render() {
let {inputValue ,inputChange,clickButton,list} = this.props;
return (
<div>
<div>
<input value={inputValue} onChange={inputChange} />
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}
2,组件无状态化,就是改为一个方法,大大提高性能:
const TodoList =(props)=>{
let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改
return (
<div>
<div>
<input value={inputValue} onChange={inputChange} />
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}
最终的代码:
import React from 'react';
import {connect} from 'react-redux'
const TodoList =(props)=>{
let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改
return (
<div>
<div>
<input value={inputValue} onChange={inputChange} />
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}
const stateToProps = (state)=>{
return {
inputValue : state.inputValue,
list:state.list
}
}
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
},
clickButton(){
let action = {
type:'add_item'
}
dispatch(action)
}
}
}
export default connect(stateToProps,dispatchToProps)(TodoList);