React 学习笔记
第一部分,腾讯课堂react入门课程
课程地址
React第一篇 npm、yarn介绍
React第二篇 React语法
React第三篇 单页应用开发
React第四篇 路由&Hooks
React第五篇 综合应用(登录+列表)
第二部分,Redux课程
课程地址
React第六篇 Redux
React第七篇 React Redux

redux工作流


安装
- redux安装:npm install redux --save
- 调试工具Redux DevTools安装:chrome应用商店下载
1. 基础入门
- 在src下新建reduxdemo.js
- 在src下新建reduxdemo/store文件夹存放数据存储代码
- reduxdemo/store下先编写index.js
- reduxdemo/store下新建reducer.js存放数据
- 通过按钮操作观察UI与数据的变化情况
src/reduxdemo.js
import React from 'react'
import 'antd/dist/antd.css'
import {Input,Button,List} from "antd";
import './redux.scss'
import store from './reduxdemo/store/index'
/**
* @Description: redux基础应用
* @author dongshuhuan
* @date 2020-05-18
*/
export default class ToDoList extends React.Component{
constructor(props){
super(props)
// console.log(store.getState())
//1.通过store(reducer)给state赋值
this.state = store.getState();
//4.订阅 并改变组件状态 注意 store<=>组件 数据变化
store.subscribe(this.storeChange.bind(this))
}
storeChange(){
this.setState(store.getState())
}
//2. 处理输入事件
handleInputChange = (event) =>{
let val = event.target.value;
console.log(val)
const action = {
type:'inputChange',
value:val
}
//3. 接收到了input值,传递给了reducer
store.dispatch(action)
}
// 5.dispatch处理按钮事件
handleAdd = () =>{
const action = {type:'addItem'}
store.dispatch(action)
}
//6. 删除事件 带参传入action
deleteItem (index){
const action = {type:'deleteItem',index}
store.dispatch(action)
}
render() {
return <div className={'container'}>
<div>
<Input placeholder={this.state.placeHolder}
style={{width:'250px'}}
onChange={this.handleInputChange}
value={this.state.inputValue}/>
<Button className={'button'} type={"primary"} onClick={this.handleAdd}>添加工作项</Button>
</div>
<div className={'list'}>
<List bordered
dataSource={this.state.list}
renderItem={(item,index)=>(
<List.Item onClick={this.deleteItem.bind(this,index)}>{item}</List.Item>
)}/>
</div>
</div>
}
}
reduxdemo/store/index.js
/**
* @Description: store仓库创建
* @author dongshuhuan
* @date 2020-05-18
*/
import {createStore} from 'redux'
import reducer from './reducer'
//reducer 注入
//window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() chrome配置redux插件拓展
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default store
reduxdemo/store/reducer.js
/**
* @Description:
* @author dongshuhuan
* @date 2020-05-18
*/
const defaultState = {
inputValue:'',
placeHolder:'输入内容',
list : [
'早九点开晨会,分配任务',
'中午12点吃饭',
'中午12点半,睡觉',
'中午1点半,起床下午工作'
]
}
export default (state=defaultState,action)=>{
console.log(state,action)
//Reducer里只能接收state,不能改变state
// 4.处理action 返回新的state
if (action.type==='inputChange') {
let newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
if (action.type==='addItem'){
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue=''
return newState
}
if (action.type==='deleteItem'){
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1)
return newState
}
return state
}
2.代码优化
面向企业级开发的代码优化
2.1 action_types 将action类型进行封装
- reduxdemo/store下新建action-types.js定义操作
- 在reducer.js和reduxdemo.js中使用action-types中的actions
- 这样做的好处时有效预防拼写错误 action-types.js
export const INPUTCHANGE = 'inputChange'
export const ADD_ITEM = 'addItem'
export const DELETE_ITEM = 'deleteItem'
reducer.js中修改
import {INPUTCHANGE,ADD_ITEM,DELETE_ITEM} from './action-types'
...
if (action.type==='inputChange') -> (action.type===INPUTCHANGE)
reduxdemo.js中修改
import {INPUTCHANGE,ADD_ITEM,DELETE_ITEM} from './action-types'
...
handleAdd = () =>{
const action = {type:'addItem'}
store.dispatch(action)
}
//修改为如下代码↓ ↓ ↓
handleAdd = () =>{
const action = {type:ADD_ITEM}
store.dispatch(action)
}
2.2 actionCreator 将reduxdemo.js中的action操作进行封装
- reduxdemo/store下新建action-creators.js封装操作
- reduxdemo.js中使用action-creators中封装的方法
action-creators.js
import {INPUTCHANGE,ADD_ITEM,DELETE_ITEM} from './action-types'
export const changeInputAction = (val) =>({
type:INPUTCHANGE,
value:val
})
export const addItemAction = () =>({
type:ADD_ITEM
})
export const deleteItemAction = (index) =>({
type:DELETE_ITEM,
index
})
reduxdemo.js中修改
import {changeInputAction} from './reduxdemo/store/action-creators'
...
//2. 处理输入事件
handleInputChange = (event) =>{
let val = event.target.value;
const action = {
type:INPUTCHANGE,
value:val
}
store.dispatch(action)
//修改为如下代码↓ ↓ ↓
//7 action-creators封装action操作
const action = changeInputAction(val)
store.dispatch(action)
}
...
2.3 分离UI
- 新建reduxdemoui.js ,只编写UI代码,将reduxdemo中的UI代码拷贝过来
- 修改reduxdemo 将UI部分替换为reduxdemoui中的TodoListUI组件
- reduxdemo中为TodoListUI传递属性,同时TodoListUI接收参数修改为props
- 用无状态组件方式实现,将reduxdemoui.js修改为无状态组件
reduxdemoui.js
// export default class ToDoListUI extends React.Component{
//
// render() {
// return (
// <div className={'container'}>
// <div>
// <Input placeholder={this.props.placeHolder}
// style={{width:'250px'}}
// onChange={this.props.handleInputChange}
// value={this.props.inputValue}/>
// <Button className={'button'} type={"primary"} onClick={this.props.handleAdd}>添加工作项</Button>
// </div>
// <div className={'list'}>
// <List bordered
// dataSource={this.props.list}
// renderItem={(item,index)=>(
// <List.Item onClick={()=>this.props.deleteItem(index)}>{item}</List.Item>
// )}/>
// </div>
// </div>
// )
// }
//
// }
//无状态组件
export default function TodoListUI(props) {
return <div className={'container'}>
<div>
<Input placeholder={props.placeHolder}
style={{width:'250px'}}
onChange={props.handleInputChange}
value={props.inputValue}/>
<Button className={'button'} type={"primary"} onClick={props.handleAdd}>添加工作项</Button>
</div>
<div className={'list'}>
<List bordered
dataSource={props.list}
renderItem={(item,index)=>(
<List.Item onClick={()=>props.deleteItem(index)}>{item}</List.Item>
)}/>
</div>
</div>
}
reduxdemo.js
render() {
// 8 封装UI代码
return <ToDoListUI inputValue = {this.state.inputValue}
handleInputChange = {this.handleInputChange}
placeHolder = {this.state.placeholder}
list = {this.state.list}
handleAdd = {this.handleAdd}
deleteItem = {this.deleteItem}/>
}
2.4 axios异步请求与redux的结合
reduxdemo.js进行网络请求
//9 axios异步请求与redux的结合
componentDidMount() {
//接口不可用,仅作为演示使用
// const url = 'https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList';
axios.get('todolist.json')
.then((res)=>{
console.log(res.data.data)
if (res.data.code===0){
const data = res.data.data;
const action = getListAction(data);
store.dispatch(action)
// console.log(data)
}
})
}
action-types.js
export const GET_LIST = 'getList'
action-creators.js
export const getListAction = (data) =>({
type:GET_LIST,
data
})
reducer.js
if (action.type===GET_LIST){
console.log('网络请求')
let newState = JSON.parse(JSON.stringify(state))
const data = action.data
newState.list = [...newState.list,...data]
return newState
}
效果图

3.redux 中间件

3.1 Redux-thunk
- 安装 npm install --save redux-thunk 或 yarn add redux-thunk --save
- store/index.js中导入redux-thunk
- action-types.js中增加数据请求函数
- reduxdemo.js导入网络请求函数并使用它
store/index.js
//thunk 中间件使用
//增强函数
//createStore只能传递两个参数,为保证chrome浏览器redux插件正常使用,需要使用增强函数
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
action-types.js
// react-thunk 中间件方式
export const getTodoList = () =>{
return(dispacth)=>{
axios.get('todolist.json')
.then((res)=>{
console.log(res.data)
if (res.data.code===0){
const data = res.data.data;
const action = getListAction(data);
dispacth(action)
}
})
}
};
reduxdemo.js
//9 axios异步请求与redux的结合
componentDidMount() {
//10.action-creators中 中间件进行异步请求网络
const action = getTodoList();
store.dispatch(action)
}
3.2 redux-saga
- 安装 npm install --save redux-saga 或 yarn add redux-saga --save
- store/index.js中引入sega
- action-types.js和action-creators.js添加代码
- 编写sagas.js
- reactdemo.js中导入和使用
store/index.js
import createSagaMiddleware from 'redux-saga'
//redux-sega
const segaMiddleware=createSagaMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose;
const enhancer = composeEnhancers(applyMiddleware(segaMiddleware));
const store = createStore(
reducer,
enhancer)
segaMiddleware.run(mySagas)
export default store
action-types.js
export const GET_MY_LIST = 'getMyList'
action-creators.js
//redux-saga 中间件方式请求数据
export const getMyListAction =()=>({
type:GET_MY_LIST
});
sagas.js
import {takeEvery,put} from 'redux-saga/effects'
import {GET_MY_LIST} from './action-types'
import axios from "axios";
import {getListAction} from "./action-creators";
//generator函数
function* mySaga() {
yield takeEvery(GET_MY_LIST,getList)
}
function* getList() {
const res = yield axios.get('todolist.json');
const action = getListAction(res.data.data)
yield put(action)
}
export default mySaga
reduxdemo.js
//9 axios异步请求与redux的结合
componentDidMount() {
//11. redux-saga中间件处理网络请求
const action = getMyListAction();
store.dispatch(action)
}