Browse Source

Merge pull request 'home' (#108) from home into master

Reviewed-on: #108
master
jingxun 2 years ago
parent
commit
cbc2b70b64
  1. 117
      day29/note/lesson101_demo_react_redux_full.md
  2. 113
      day29/note/lesson102_demo_react_redux_async_action.md
  3. 34
      day29/note/lesson103_understand_react_redux.md
  4. 114
      day29/note/lesson104_connect_ui_and_container.md
  5. 134
      day29/note/lesson105_use_react_redux_basic.md
  6. 93
      day29/note/lesson106_simple_map_dispatch.md
  7. 82
      day29/note/lesson107_provide_component.md
  8. 69
      day29/note/lesson108_UI_and_container_component.md
  9. 67
      day29/note/lesson109_share_data_person_component.md
  10. 41
      day29/react-sum/package.json
  11. 11
      day29/react-sum/public/index.html
  12. 14
      day29/react-sum/src/App.js
  13. 19
      day29/react-sum/src/containers/Person/index.jsx
  14. 51
      day29/react-sum/src/containers/Sum/index.jsx
  15. 12
      day29/react-sum/src/index.js
  16. 0
      day29/react-sum/src/redux/actions/person.js
  17. 9
      day29/react-sum/src/redux/actions/sum.js
  18. 2
      day29/react-sum/src/redux/constant.js
  19. 0
      day29/react-sum/src/redux/reducers/person.js
  20. 14
      day29/react-sum/src/redux/reducers/sum.js
  21. 5
      day29/react-sum/src/redux/store.js
  22. 8699
      day29/react-sum/yarn.lock

117
day29/note/lesson101_demo_react_redux_full.md

@ -0,0 +1,117 @@
# 求和案例`redux`完整版
上节课我们完成了用很精简的方式来完成了`redux`版的求和案例,但是上节课那是精简版的,省略了创建动作对象那一步,而且整个过程也没有严格地遵守规范,那么这节课我们来完整地用`redux`来实现我们之前的哪个案例。
## 回顾
在我们开始这节课的内容之前,我们先对上节课的内容来做一个简单的回顾。
- `subscribe API`来监测`redux`中的`state`
- `getState API`来获取`redux`中的`state`
- `createStore`来创建`store`
- `reducer`本质上是一个函数,接收之前的状态和动作对象
- `redux`中不关心其他的东西,制作基础操作
- `redux`中的`state`改变不重新调`render`
以上便是上节课的主要要点,接下来我们来开始这节课的学习。
## `Action Creator`
我们之前精简版来使用`redux`的时候是不是直接手写的动作对象啊?但是呢样的话不符合`redux`的使用规范,正常的流程是什么样子的呢?我们是需要由组件发起,然后由`Action Creators`来创建一个动作对象,然后再通过`dispatch`分发出去的。那么我们现在既然要用到`Action Creators`了,我们是不是要创建一个文件来专门为我们创建动作对象啊?
而且我们看见了这个`Action Creators`也是复数,那么是不是和`Reducers`一样?每个组件要有一个对应的`Action Creator`,所以说我们就要在`redux`文件夹下创建一个`sum_action_creator.js`文件。我们这里说一下,因为`redux`也不是组件,所以说要在`src`下创建一个文件夹专门用于存放`redux`相关的文件。
那么现在我们文件创建好了,依然还是之前那样,我们不知道如何来创建一个动作对象啊。而且这个`creator`的本质上又是什么呢?我们来想一想,我们这个`creator`是用来干什么的?是不是专门为`Sum`组件创建动作对象的?那么我们的`Sum`组件都有哪些动作?是不是加和减?那么我们这里的`creator`只要创建了动作对象之后是不是要交出去给`dispatch`?那么是不是就意味着要返回一个对象给`dispatch`做参数啊?那么直接用函数就可以完成了啊。
```js
export const createIncrementAction = data => ({ type: 'increment', data });
export const createDecrementAction = data => ({ type: 'decrement', data });
```
大家来看代码,这是什么意思?我们是不是分别暴露了两个函数,这两个函数都是箭头函数,箭头函数都接收一个参数就是我们要操作的对象,然后我们返回一个对象,这个对象就是我们的动作对象,有人可能说这两个函数长得这么像,我们给写成一个函数吧,当然可以,但是我们先这么写,我们一步一步来,我们这个案例会一步一步地进行版本迭代,到了最后一个版本我们会给大家呈现出一个最规范的版本。现在就先这么写,不要给自己太大的压力,先把族简单的原理搞懂。
那么我们再来问大家,我这个箭头函数问什么要用小括号?我们是不是要返回一个对象?对象是不是以花括号来作为边界的?而函数体是不是也是以花括号作为边界的?如果我们这里不用小括号把对象括起来的话,箭头函数就默认你这不是对象,而是函数体。
## 创建动作对象
现在我们已经写好了我们的`Action Creator`了,那么我们怎么用?首先这个`Action Creator`是由谁触发的?是不是我们的组件触发这里的`Creator`来创建动作对象的?那么我们回到组件中来:
![image-20220119084518936](https://file.lynchow.com/2022-01-19-004522.png)
看看我们这是在干什么啊?我们是不是自己手写了一个动作对象交给了`dispatch`啊?现在呢我们既然已经准备好了`Creator`那么我们还要这么写吗?我们是不是直接调用我们的`Creator`来创建动作对象然后再交给`dispatch`就行了?
```jsx
import React, { Component } from 'react'
import store from '../../redux/store';
import { createIncrementAction, createDecrementAction } from '../../redux/sum_action_creator';
export default class Sum extends Component {
componentDidMount() {
store.subscribe(() => this.setState({}));
}
render() {...}
add = () => {
const { value } = this.selectNum;
store.dispatch(createIncrementAction(value * 1));
}
dec = () => {
const { value } = this.selectNum;
store.dispatch(createDecrementAction(value * 1));
}
odd = () => {
const { value } = this.selectNum;
if (store.getState() % 2 !== 0) store.dispatch(createIncrementAction(value * 1));
}
async = () => {
const { value } = this.selectNum;
setTimeout(() => {
store.dispatch(createIncrementAction(value * 1));
}, 1000)
}
}
```
我们来看代码,首先这些函数是不是都在`sum_action_creator.js`里啊?那么我们就得先导入,然后再之前手写动作对象的地方直接调用函数。这样的话我们就可以通过我们的`Action Creator`来创建我们的动作对象了。那么我们再来看一下效果吧
![iShot2022-01-19 08.54.36](https://file.lynchow.com/2022-01-19-005529.gif)
我们来看一下,这次我们是不是也可以完成我们想要的效果。
## 常量模块
可能大家以为我们到上面那一步我们的完整版就写完了,但是我们有一个问题,当然并不是代码上的问题,我们来想一下,我们的`reducer`里面匹配动作的`type`以及我们创建动作对象里面的`type`是不是都是我们手写的?那么如果不小心单词写错了怎么办?那我们不就匹配不上了嘛。所以说我们要添加一个常来那个模块在`redux`下创建一个`constant.js`文件
```js
// constant.js
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
// sum_action_creator.js
import { INCREMENT, DECREMENT } from "./constant"
export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
// sum_reducer.js
import { INCREMENT, DECREMENT } from './constant';
export default function sumReducer(preState, action) {
if (preState === undefined) preState = 0;
const { type, data } = action;
switch (type) {
case INCREMENT:
return preState + data;
case DECREMENT:
return preState - data;
default:
return preState;
}
}
```
我们来看代码,我们在`constant.js`里面定义了两个常量,然后分别暴露出来,然后再用到的地方直接导入常量,这样我们就不会因为单词不小心写错导致功能不起作用了。
## 总结
- `Action Creators`本质上其实也是函数
- 我们通过常量模块来定义动作类型,防止单词写错

113
day29/note/lesson102_demo_react_redux_async_action.md

@ -0,0 +1,113 @@
# 求和案例之异步`action`
上节课我们介绍了`redux`的完整流程中的`Action Creators`这个环节,我们到这里其实已经可以通过`redux`来对组件的状态来进行集中化管理了,但是我们里面还有一些高级操作,这节课我们就来先介绍其中的一个,叫异步`action`。
## 回顾
那么在介绍异步`action`之前呢,我们来对上节课的内容来做一个简单的回顾:
- `Action Creators`本质上其实也是函数
- 我们通过常量模块来定义动作类型,防止单词写错
以上便是上节课的主要要点,接下来我们来介绍异步`action`
## 什么是异步`action`
我们之前在课上说,我们的这个`action`并不是什么高大上的东西,其实哪个玩意儿就是仅仅只是一个原生`js`中的一个`object`类型的普通对象,我们甚至都不需要通过`Action Creator`来创建,直接手写都行,但是现在我来告诉大家,这个`action`,不仅仅可以是一个普通对象,他还可以是一个函数。我们管这种一般对象类型的`action`叫做同步`action`,而那种函数类型的`action`,我们称之为异步`action`。
## 需求描述
我们说了上面这两个概念,大家是不是觉得很懵,什么意思?不理解啊。我们先别着急,我们通过案例来去理解。
我们先来思考一个问题,我们页面上的异步加的功能是怎么实现的?是不是通过`setTimeout`方法配合一个执行器函数来完成的?我们代码中是要延迟 1 秒之后再加,那么我来问一个问题,这个延迟的 1 秒钟的时间是在哪里延迟的?是不是在我们的组件中?那么现在我们要求这个等待的事件不在组件中发生,我们要把等待的事件交给`action`。
## 需求分析
我们要求等待事件交给`action`的话,那么我们组件中是不是就不能再调用`setTimeout`函数了?但是不调用`setTimeout`的话我们不就不等待了嘛,那么我们是不是就要在`Action Creator`上面做手脚了?那么我们就不能用之前的函数了啊,我们就需要重新定义一个新的函数。
## 实现
我们现在思路已经有了,那么怎么实现呢?
```jsx
// sum_action_creator.js
import { INCREMENT, DECREMENT } from "./constant"
import store from "./store";
export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
export const createAsyncIncrementAction = (data, time) => {
return () => setTimeout(() => {
store.dispatch(createIncrementAction(data))
}, time);
}
// Sum 组件
import React, { Component } from 'react'
import store from '../../redux/store';
import {
createIncrementAction,
createDecrementAction,
createAsyncIncrementAction
} from '../../redux/sum_action_creator';
export default class Sum extends Component {
componentDidMount() {
store.subscribe(() => this.setState({}));
}
render() {...}
...
async = () => {
const { value } = this.selectNum;
store.dispatch(createAsyncIncrementAction(value * 1, 1000));
}
}
```
我们来看代码,我们在组件中调用了`createAsyncIncrementAction`函数,而且传了数据和要延迟的时间。但是我们有这个函数吗?是不是没有?那么我们就要在`sum_action_creator.js`中来定义这个函数,那么我们来看这个函数的代码。首先我们接收数据和要延迟的时间这两个参数。然后返回一个函数。为什么我们要返回一个函数?因为我们要写一个异步`action`,那么为什么异步`action`就要返回一个函数呢?因为函数中可以开启异步任务,像对象啊,数组啊什么的都没法开启异步任务,所以说异步`action`得返回一个函数。
那么这个函数里面我们做什么呢?首先是不是要开启一个定时器?等延迟的时间到了之后我们做什么?是不是把动作分发出去,那么就交给`store`吧,交给`store`直接分发一个动作对象给`reducer`就行了啊,那么动作对象从哪来?创建动作对象我们是不是早就写好了?直接调用`createIncrementAction`然后把数据传进去就可以了。那么我们来看一下效果吧。
![image-20220119100852151](https://file.lynchow.com/2022-01-19-020854.png)
报错了告诉我们`action`必须是一个普通对象,我们需要通过一个中间件来实现。因为`store`这个东西是不支持分发函数的,所以说我们需要通过一个中间件来使得`store`接收到函数之后不分发给`reducer`,而是直接执行这个函数。我们看我们的`action`,这个`action`返回的是一个函数,这个函数也没有调用,那么这么往下进行啊?是不是要`store`来执行这个函数?而`store`执行函数之后马上开启一个定时器,等时间到了之后重新创建一个加的`action`交给`store`再由`store`分发给`reducer`,那么要使用什么中间件呢?
我们来安装一下`npm i redux-thunk`,我们就是靠这个中间件来实现让`store`来调用我们的异步`action`
```js
import { createStore, applyMiddleware } from 'redux';
import sumReducer from './sum_reducer';
import thunk from 'redux-thunk';
export default createStore(sumReducer, applyMiddleware(thunk));
```
我们来看一下代码,我们的这个中间件是不是作用于`store`上的?那么好,我们是不是要把这个中间件在`store`中导入?然后我们怎么能使得这个`store`能够正常使用这个中间件呢?我们就要通`redux`的一个函数`applyMiddleware`来在创建`store`的时候就来实现中间件的加载。那么我们这次再来看一下效果吧:
![iShot2022-01-19 10.26.18](https://file.lynchow.com/2022-01-19-022657.gif)
这一次我们的效果就正常实现了。
但是呢我们的代码还有一个不完善的地方。我们是不是在定时器的执行器函数中通过`store`来调用了`dispatch`?这样的话我们就要引入`store`,但是实际上呢我们的这个`action`返回的函数是不是`store`调用的?`store`在调用这个函数的时候就已经把`dispatch`函数传过去了,只是我们没有接,那么我们来做一下修改:
```js
import { INCREMENT, DECREMENT } from "./constant"
export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
export const createAsyncIncrementAction = (data, time) => dispatch => setTimeout(
() => dispatch(createIncrementAction(data)), time
);
```
这样的话我们连`store`都不需要再引入了。
## 总结
- 异步操作不想在组件自身操作时交给异步`action`
- 异步`action`中通常会调用同步`action`
- 异步`action`需要中间件`redux-thunk`的支持

34
day29/note/lesson103_understand_react_redux.md

@ -0,0 +1,34 @@
# 对`react-redux`的理解
上节课我们介绍了异步`action`,对中间件也做了一点介绍,我们知道异步`action`需要通过中间件的支持才能正常使用。这节课我们来介绍一个库`react-redux`。
## 回顾
在开始这节课的内容之前,我们对上节课的内容来做一个简单的回顾
- 异步操作不想在组件自身操作时交给异步`action`
- 异步`action`中通常会调用同步`action`
- 异步`action`需要中间件`redux-thunk`的支持
以上便是上节课的主要要点,接下来我们开始对这节课内容的学习。
## `react-redux`
在开发中,很多开发者在使用`react`的时候都喜欢在`redux`中来做集中式的状态管理,然后`react`官方团队发现了这一现象之后呢,就自己开发了一款`react-redux`库,之前我们说`redux`库和`react`没有半毛钱关系,但是现在的`react-redux`库可就不一样了,这个库是`react`的一个扩展库。
但是我们现在不是已经成功使用上了`redux`了嘛,我们还要`react-redux`干什么呢?我们使用`redux`的整个流程我们确实已经都打通了,我们也确实已经可以用`redux`来对我们的`react`组件来做集中化状态管理了。但是如果我们用`react-redux`库的话,我们可以更加方便地在我们的`react`项目中来使用我们的`redux`。
![react-redux模型图](https://file.lynchow.com/2022-01-19-024915.png)
我们先来看模型图。首先`react-redux`库把组件分为两类,一类是`UI`组件,另一类叫容器组件。我们的所有的`UI`组件都是容器组件的子组件。而容器组件才是真正和`redux`打交道的组件,但是`UI`组件不可以使用`redux`,只是对页面来做呈现应的。
但是如果`UI`组件中要获取`redux`中存放的`state`,或者修改`redux`中存放的`state`的话都要通过容器组件,容器组件会通过`props`来把`UI`组件所需要的`state`和修改`state`的方法都传给`UI`组件
## 总结
- `react-redux`库把组件分为两类
- `UI`组件
- 容器组件
- 只有容器组件可以直接操作`redux`
- `UI`组件必须通过容器组件来操作`redux`
- `UI`组件所需要的`redux`中的状态以及操作状态的方法都要由容器组件通过`props`来交互

114
day29/note/lesson104_connect_ui_and_container.md

@ -0,0 +1,114 @@
# 连接容器组件和`UI`组件
上节课我们简单介绍了一下`react-redux`库,讲到了`react-redux`这个库把项目中的组件分为`UI`组件和容器组件,那么这节课我们就来介绍一下如何来连接`UI`组件和容器组件。
## 回顾
在开始这节课的学习之前,我们把上节课的内容来简单回顾一下
- `react-redux`库把组件分为两类
- `UI`组件
- 容器组件
- 只有容器组件可以直接操作`redux`
- `UI`组件必须通过容器组件来操作`redux`
- `UI`组件所需要的`redux`中的状态以及操作状态的方法都要由容器组件通过`props`来交互
以上便是上节课的主要内容,接下来我们开始这节课的学习。
## `UI`组件改写
我们来想一下我们之前的案例中组件都在哪?是不是在`components`文件夹中?通常我们在编写`react`项目的时候,`UI`组件都会放在这里,而容器组件都放在`container`文件夹中,那么我们先来把我们的`Sum`组件改成一个`UI`组件吧。那么,既然是`UI`组件的话,我们的组件中是不是就不能有关于`redux`的任何东西的存在?
```jsx
import React, { Component } from 'react'
export default class Sum extends Component {
render() {
return (
<div>
<h1>当前求和为:???</h1>
<select ref={c => this.selectNum = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.dec}>-</button>
<button onClick={this.odd}>odd</button>
<button onClick={this.async}>async</button>
</div>
)
}
add = () => {
const { value } = this.selectNum;
}
dec = () => {
const { value } = this.selectNum;
}
odd = () => {
const { value } = this.selectNum;
}
async = () => {
const { value } = this.selectNum;
}
}
```
现在我们是不是把所有的`redux`相关的内容全部删掉了?但是现在我们的所有功能是不是都没法用了?因为我们到现在还没有去实现我们的容器组件,所以说我们目前还是不能去操作`redux`的。
## 创建容器组件
现在我们已经成功地把我们之前的组件改成一个`UI`组件了,那么大家说接下来要干什么?是不是要给我们的`UI`组件准备一个容器组件?只有有了容器组件我们才能跟`redux`来进行交互啊。那么我们来创建一个容器组件吧。但是我们的容器组件怎么写呢?直接定义一个类组件?那肯定不行的啊,我们说了这个容器组件是直接和`redux`打交道的组件,那么这里的组件不能靠我们自己写,我们就需要借助`react-redux`来生成这个组件。那么我们是不是要安装这个库?`npm i react-redux`,安装完这个库我们来创建这个组件吧。
```jsx
import SumUI from '../../components/Sum';
import store from '../../redux/store';
import {connect} from 'react-redux';
export default connect()(SumUI);
```
这段代码是什么意思呢?我们来想一下,容器组件是干什么的?是不是用来使得`UI`组件可以间接性地使用和操作`redux`?这是不是相当于一个桥梁的作用?那么这座桥的两端是什么?是不是`UI`组件和`redux`?所以说我们先不管别的,最起码我们得先把我们这个桥的两端引入进来吧?所以第一行引入`Sum`组件。
那么第二行我们引入`redux`,我们之前在案例中是不是已经写好了我们的`redux`相关的东西,总共 4 个文件,那么我们怎么引入?我们用`redux`是不是一直都是`store`在居中调度?不管分发动作也好,还是存储`state`也好,我们是不是都是`store`在做这部分内容?我们有直接来调用`reducer`吗?是不是根本没有?那么这样的话,我们是不是只要引入`store`就可以了?
然后后桥梁的两端都有了,是不是要把桥搭起来?这个桥怎么搭?我们说要用`react-redux`来搭,我们`react-redux`中有一个函数叫`connect`,这个函数很好理解,桥梁是用来连接河两岸的嘛。所以说我们调用这个函数,而这个函数的返回值是一个函数,这个`API`设计的就是让我们柯里化调用,当我们柯里化调用了这个函数之后,我们就可以生成一个所谓的容器组件。但是这个容器组件是谁的容器组件呢?所以说我们要把我们的`UI`组件传给这个函数的返回函数。
但是我们现在先不把代码写完整,我们一步步来,我们先让容器组件和`UI`组件先建立联系。那么我们现在就还用不到`store`,那么我们生成了一个容器组件了,是不是要把这个容器组件暴露出去?所以最后我们`export`一下。
那么当我们创建了容器组件,而且容器组件里面还有`UI`组件来做子组件,那么我们是不是就要在页面上来渲染容器组件啊?不然的话我们就等于没有创建容器组件啊,一个组件你创建了不渲染,那还创建他干什么呢。所以我们修改一下`App`组件中的引入就可以了。那么我们来看一下效果吧:
![image-20220119114627417](https://file.lynchow.com/2022-01-19-034629.png)
诶呦,报错了。这是为啥呢?报的什么错啊?说找不到`store`,为什么找不到?我们说了容器组件是干什么的?是不是连接`UI`组件和`redux`的一个桥梁?我们把`UI`组件交给了容器组件,但是我们实现连接`store`了吗?是不是没有?但是我们是不是引入了`store`了?但是为什么这里还会提醒我们说找不到`store`呢?
因为容器组件要得到的`store`不能由开发者手动去引入。所以我们把引入`store`那一步给删掉。但是可能有人说了,那么我们引入了都找不到,删掉不就更找不到了嘛。别急,我来问一下,这个`store`是哪个组件要用?是不是容器组件?容器组件有没有渲染?是不是在`App`组件中渲染了?我们从`App`组件中把`store`通过`props`传给容器组件才是正确的方式。那么我们来改一下吧:
```jsx
import React, { Component } from 'react'
import Sum from './containers/Sum'
import store from './redux/store'
export default class App extends Component {
render() {
return (
<div>
<Sum store={store}/>
</div>
)
}
}
```
我们在`App`组件中引入`store`,然后传给容器组件,来看一下效果:
![image-20220119115659387](https://file.lynchow.com/2022-01-19-035702.png)
这次就正常展示了页面了。以上呢就成功将`UI`组件和容器组件成功连接起来了
## 总结
- 容器组件是连接`UI`组件和`redux`的桥梁
- 容器组件要接收到`store`,但是必须通过`props`传入,不可以手动引入
- 用`react-redux`提供的`connect`高阶函数来创建容器组件

134
day29/note/lesson105_use_react_redux_basic.md

@ -0,0 +1,134 @@
# `React-redux`的基本使用
上节课我们介绍了如何将容器组件和`UI`组件连接起来,这节课我们来介绍一下`react-redux`的基本使用。
## 回顾
在开始这节课的内容之前,我们对上节课的内容做一个简单的回顾
- 容器组件是连接`UI`组件和`redux`的桥梁
- 容器组件要接收到`store`,但是必须通过`props`传入,不可以手动引入
- 用`react-redux`提供的`connect`高阶函数来创建容器组件
以上便是上节课的主要要点,接下来我们来进行这节课的学习。
## 验证容器组件与`UI`组件的父子关系
我们之前得代码中是不是已经引入了`UI`组件?而且是不是也已近创建了容器组件?其实`store`是不是也已经在了,只不过我们还没有进行连接,那么我们接下来是不是就要让这座桥正式生效来让`UI`组件和`redux`连接起来了呢?先不忙,我们先来验证一下`UI`组件和容器组件的关系。
那么容器组件和`UI`组件是什么关系?我们之前在分析原理图的时候,是不是就已经说过了,`UI`组件是容器组件的子组件。那么我们来验证一下他们之间的父子关系是不是真的。但是怎么验证呢?
![image-20220119121446402](https://file.lynchow.com/2022-01-19-041448.png)
我们来打开控制台,然后切换到开发者工具中,这里是不是很明显就可以看出来这两个是父子关系?
## `UI`组件展示
那么我们现在也验证了容器组件和`UI`组件的父子关系了。那么我们页上展示的数据从哪来?我们之前的案例中是不是直接从`redux`的`state`里面读出来的?但是现在我们还没有连接`redux`,而且`UI`组件负责展示,而`UI`组件不允许直接去找`redux`要数据,那么是不是要通过容器组件?那么容器组件和`UI`组件之前的通信是不是直接使用`props`就行了?
那么我们怎么从容器组件给`UI`组件传东西啊?我们打开了容器组件的代码,我们看不到`UI`组件标签的影子啊,那这还传什么`props`啊,没学过啊。
那么我们来讲一下,我们创建容器组件是不是要调用`connect`?这个函数是高阶函数?我们要柯里化调用。当我们第一次调用`connect`函数的时候,要传入两个参数:
- 这两个函数都是函数
- 第一个参数函数的返回值作为状态通过`props`传给了`UI`组件
- 第二个参数函数的返回值作为操作状态的方法通过`props`传给`UI`组件
那么我们来看一下:
```jsx
import SumUI from '../../components/Sum';
import { connect } from 'react-redux';
const data = () => ({ a: 100 });
const method = () => ({ b: () => console.log(123) });
export default connect(data, method)(SumUI);
```
我们来看上面这段代码,什么意思?第一个函数我们是不是返回一个对象?这个函数是不是我们`connect`函数第一次调用的时候传递的第一个参数?那么这个函数返回的对象是不是就作为状态通过`props`传给了`UI`组件。为什么要返回一个对象啊?我们这里是不是通过`props`传的?那么通过`props`传的话,不是对象我们在`UI`组件通过什么`key`来取这个值呢?那么为什么说是作为状态呢?因为这个东西是存在`redux`中的状态。但是我们现在是不是还没有连接`redux`啊,所以说先手写一个出来。
那么第二个函数则是第二个参数,这个函数呢也返回一个对象,这个对象的`key`也就作为`props`的`key`,而`value`就是一个函数,这个函数是用来操作状态的方法。那么我们在`UI`组件中来打印一下收到的`props`吧:
![image-20220119123921189](https://file.lynchow.com/2022-01-19-043923.png)
是不是我们传的东西都传进去了,而且,`react-redux`还自动传了个`store`。我们下不用管这些。那么我们现在是不是能通过这种方法来让我们的`UI`组件拿到初始值来做页面的展示了啊?那么我代码就不贴了,大家改一下就行了。
## 获取`state`
我们现在已经可以通过容器组件给`UI`组件传递数据和方法了,那么我们现在传给`UI`组件的数据是不是都是我们写死的?但是我们实际过程中能写死吗?是不是不能啊?那么我们就要从`redux`中取啊,怎么取?是不是调用`getState`就行了?那么是谁来调用呢?是不是`store`来调用`getState`方法?那么我们怎么办?
我们之前说了第一个参数函数的返回值作为状态传给`UI`组件,那么这个参数函数是谁调用的?是不是`react-redux`调用的?而这个函数的作用是什么?是不是就只把数据传进去啊?所以说这个函数在被调用的时候`react-redux`就已经自动把`state`的值传给函数了。所以说我们直接在这个函数接一下,就可以了,那么我们来看一下:
```js
import SumUI from '../../components/Sum';
import { connect } from 'react-redux';
const data = state => ({ sum: state });
const method = () => ({ cons: () => console.log(123) });
export default connect(data, method)(SumUI);
```
我们这里稍微显得规范一点,把`key`给改了一下。那么这个时候我们的页面是不是就可以拿到我们的值了?那么页面上不就可以正常展示了嘛:
![image-20220119125344553](https://file.lynchow.com/2022-01-19-045347.png)
我们来看一下,这次我们直接从`redux`中拿到了状态值展示到了页面上。那么这是不是意味着我们已经成功通过容器组件将`UI`组件和`redux`连接起来了?如果没有连接起来的话,我们还能在`UI`组件中拿到`redux`中的值吗?但是我们的`UI`组件中我们有调用任何跟`redux`有关的东西吗?有任何`redux`的`API`吗?是不是没有?
## 实现动作
那么我们继续往下看,我们是不是也给`UI`组件传了一个方法?我们传的方法是一个打印 123 的函数,那么我们在`UI`组件中来用一下试试能不能用。我们把`+`按钮的`onClick`事件的回调换成了`this.props.cons`,那么我们点击一下按钮来看一下效果:
![image-20220119130100948](https://file.lynchow.com/2022-01-19-050102.png)
也成功打印了。那么我们是不是就意味着我们把我们所有的动作都通这个方法来实现就行了呢?
```jsx
// Sum 容器组件
import SumUI from '../../components/Sum';
import { connect } from 'react-redux';
import { createIncrementAction } from '../../redux/sum_action_creator';
const data = state => ({ sum: state });
const method = dispatch => ({ increment: data => dispatch(createIncrementAction(data)) });
export default connect(data, method)(SumUI);
// Sum UI组件
export default class Sum extends Component {
render() {
return (
<div>
...
<button onClick={this.add}>+</button>
...
</div>
)
}
add = () => {
const { value } = this.selectNum;
this.props.increment(value * 1)
}
...
}
```
我们来看一下代码,我们直接在我们的容器组件中把传给`UI`组件的方法改了一下,这个方法接收一个`data`,然后把这个`data`直接传给了我们的`Action Creator`,但是我们是不是要把行动对象分发下去才能干活啊?是不是就要用`store.dispatch`?那么这个`store`从哪来?我们第一个函数是专门传`state`的,所以第一个函数可以接到`state`,那么第二个函数是专门通过动作来操作状态的,状态要由`store`分发,那么这个函数是不是就可以接收到`store`?其实不是,`react-redux`给你封装得更加方便,这里他直接把`dispatch`传给你了。
然后再来到`UI`组件中,我们的`+`按钮的回调中是不是取了我们下拉框选得值,然后再把值传给传进来的这个函数那么是不是就可以实现我们的加法操作了呢?我们来看一下效果:
![iShot2022-01-19 13.19.53](https://file.lynchow.com/2022-01-19-052017.gif)
现在我们就可以实现我们的加操作,那么其他的几个操作是不是就简单了,我们这里就不再赘述了。
## 总结
以上就是这一小节关于`react-redux`的基本使用,但是上面的代码并不规范。
- `connect`函数在第一次调用时要接收两个参数
- `connect`函数第一次调用时接收到的参数必须都是函数
- 第一个函数叫`mapStateToProps`,返回值必须是一个对象,对象的`value`是`redux`中存的状态值,作为状态通过`props`传给`UI`组件
- 第二个函数叫`mapDispatchToProps`,返回值必须是对象,对象的`value`必须是函数,作为操作状态的方法,通过`props`传给`UI`组件

93
day29/note/lesson106_simple_map_dispatch.md

@ -0,0 +1,93 @@
# 简写`mapDispatchToProps`
上节课我们介绍了`react-redux`的基本使用,在不知不觉中完成了`UI`组件和`redux`的连接。这节课我们来做一些优化的事儿。
## 回顾
在开始这节课的内容之前,我们先来对上节课的内容来做一个简单的回顾:
- `connect`函数在第一次调用时要接收两个参数
- `connect`函数第一次调用时接收到的参数必须都是函数
- 第一个函数叫`mapStateToProps`,返回值必须是一个对象,对象的`value`是`redux`中存的状态值,作为状态通过`props`传给`UI`组件
- 第二个函数叫`mapDispatchToProps`,返回值必须是对象,对象的`value`必须是函数,作为操作状态的方法,通过`props`传给`UI`组件
以上便是上节课我们的主要要点,接下来我们开始这节课的内容。
## 优化
我们之前是已经把我们的案例完整地写了一遍,但是优化空间很大,有很多地方可以优化,那么我们一个个来,我们先从我们的容器组件开始。
我们也许在初学的时候我们的代码都是用最基础的形式写成
```js
function mapStateToProps(state) {
return { sum: state };
}
function mapDispatchToProps(dispatch){
return {
increment: data => dispatch(createIncrementAction(data)) ,
decrement: data => dispatch(createDecrementAction(data)),
asyncIncrement: (data,time) =>dispatch(createAsyncIncrementAction(data,time))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SumUI);
```
这样当然是最规整的,可读性也很高,但是这么写太复杂了,我们在实际开发中肯定不会这么写,我们都会用简写方式来缩减代码量。那么我们再来看一下:
```js
const mapStateToProps = state => ({ sum: state });
const mapDispatchToProps = dispatch => ({
increment: data => dispatch(createIncrementAction(data)) ,
decrement: data => dispatch(createDecrementAction(data)),
asyncIncrement: (data,time) =>dispatch(createAsyncIncrementAction(data,time))
});
export default connect(mapStateToProps, mapDispatchToProps)(SumUI);
```
这样的话从编码的角度上来说是不是就要简单一些啊?但是这好像也没少什么东西,只是把我们的一般函数改成了箭头函数。那么我们再来做进一步简化:
```js
export default connect(
state => ({ sum: state }),
dispatch => ({
increment: data => dispatch(createIncrementAction(data)),
decrement: data => dispatch(createDecrementAction(data)),
asyncIncrement: (data, time) => dispatch(createAsyncIncrementAction(data, time))
})
)(SumUI);
```
这样其实才是在工作中大家最经常使用的情况,我们不做什么定义的操作,定义了就直接拿来用,但是这都是在编码层面上的简写,但是接下来我们再换一种写法:
```js
export default connect(
state => ({ sum: state }),
{
increment: createIncrementAction,
decrement: createDecrementAction,
asyncIncrement: createAsyncIncrementAction
}
)(SumUI);
```
可能有人要说,那你这不对了啊,不是两个参数都得是函数嘛我们课没有强制要求一定得传函数啊,这么写是什么意思我们先不管,我们先看效果,看能不能这么写,如果能,我们再分析,否则就要修改了。
![iShot2022-01-19 13.59.21](https://file.lynchow.com/2022-01-19-055953.gif)
我们来看一下,效果一切都是正常的。那么我们来分析一下问什么吧。
要想搞明白简写版,就必须搞明白原版,第一步,我们点击了`+`按钮之后,是不是就会调用我们传进去的`increment`这个`key`对应的函数?这个函数是什么?是不是我们定义的箭头函数?这个函数接收我们传的参数,然后执行函数体,创建动作对象并分发下去,然后到`reducer`执行动作,然后返回新的状态,被容器组件拿到,然后传给`UI`组件。这个流程是不是这么进行的?
那么我们再来看简写版,我们点击了`+`按钮,是不是也要掉`increment`这个`key`对应的函数?这个函数是什么?是不是我们自己写的创建动作对象的函数?那么我们传参数了吗?我们是不是把下拉框里面选中的数字传进去了,那么这一步点击事件的回调执行的东西,是不是就相当于是`createIncrementAction(data)`?那么这个函数的返回值是什么?是不是一个动作对象?那么表面上这个流程到这里是不是就停了?我们点击按钮了,传了参数了,创建了`action`对象了,但是我们没有`dispatch`。那不就停了嘛。但是我们实际效果好像是没有问题的啊。
那么这就是我们的另外一个没有提到过的点了,`react-redux`收到`action`对象之后,会自动分发。所以说我们不需要手动去分发了,`react-redux`自动就帮我们把这活儿给干了。
## 总结
- `mapDispathtoProps`可以是一个对象
- `react-redux`可以自动分发动作对象

82
day29/note/lesson107_provide_component.md

@ -0,0 +1,82 @@
# 优化`Provider`组件的使用
上节课我们介绍了如何去优化`mapDispatchToProps`函数,这节课我们继续来讲关于优化的问题。
## 回顾
在这节课内容开始之前,我们来对上节课的内容来做一个简单的回顾:
- `mapDispathtoProps`可以是一个对象
- `react-redux`可以自动分发动作对象
以上便是我们上节课的主要要点,接下来,我们来开始这节课的内容。
## `redux`状态驱动页面更新
我们先来回想一下,我们之前说过`redux`中的状态变化并不会驱动页面更新,那么我们不得已就得在组件一挂载完成的时候就让`redux`来监测状态变换,一旦变化了我们就通过修改组件`state`的方式来使得页面重新渲染
```jsx
export default class Sum extends Component {
componentDidMonut() {
store.subscribe(
() => this.setState({});
)
}
}
```
我们之前是不是这么写的?但是这么写是不是很麻烦啊?如果说这个组件没被挂载怎么办啊?我们有一个方法可以在`index.js`这个入口文件中来做处理,我们来看一下:
```js
ReactDOM.render(<App />, document.getElementById('root'));
store.subscribe(
() => ReactDOM.render(<App />, document.getElementById('root'))
);
```
这是什么意思?我们首先是不是先渲染了`App`组件?`App`组件是一切组件的父组件。这样我们可以完成一个初始页面的展示。然后我们再用`store`来监测`state`的变化,一旦变化我们直接通过`ReactDOM`将整个页面重新渲染。
可能有人要问了,你这么搞,如果页面上有几百个组件效率不会出问题吗?我们之前是不是说过有`diffing`算法啊?我们这么搞的话,即便组件很多,`diffing`算法也还是会照常进行比对的啊。所以说不会有什么影响。
但是呢我们上面这个操作是不是都是原生`redux`需要注意的点?我们现在是不是用上了`react-redux`?那么我们把这段代码删掉,我们不手动去监测`redux`里的状态了,那么我们看效果:
![iShot2022-01-19 14.34.44](https://file.lynchow.com/2022-01-19-063511.gif)
现在我们没有手动去监测,但是也可以驱动页面更新了,这是为什么呢?这就是`react-redux`为我们做的事儿了,我们的容器组件是不是通过`connect`函数创建的?我们用了`connect`函数就使得我们的容器组件自动有了监测`redux`状态变化的能力了。
## 传入`store`
那么我们还有一个问题,我们的容器组件是在哪渲染的?是不是在`App`组件里?那么我们的`App`组件里可能就只有一个组件吗?如果我们有十几个容器组件,这十几个容器组件是不是都要接收到`store`啊?那么我们这样一个个传是不是很麻烦?那么有没有一个简单的办法呢?那就要请出我们的`Provider`组件了。
`Provider`组件是`react-redux`库中提供的一个专门用来传递`store`的一个组件,那么怎么用呢?
```jsx
// App 组件
export default class App extends Component {
render() {
return (
<div>
<Sum />
</div>
)
}
}
// index.js
import store from './redux/store';
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
```
我们来看一下,我们用`Provider`来传`store`的话,那么我们的`App`组件中就不用再手动给各个子组件传`store`了。然后再来看`index.js`文件,我们是不是用`Provider`组件把`App`组件包裹住了。并且通过`props`把`store`传给了`Provider`组件,那么这个时候`Provider`组件就会同意给`App`组件中的容器组件传入`store`
## 总结
- `react-redux`自动帮我们监测`redux`中的状态变化
- `react-redux`提供了`Provider`组件来替我们批量传递`store`

69
day29/note/lesson108_UI_and_container_component.md

@ -0,0 +1,69 @@
# 整合`UI`组件和容器组件
上节课我们介绍了页面更新与`stroe`传递的优化,这节课我们来进行最后一个优化项。
## 回顾
在开始这节课的内容之前我们来对上节课的内容来做一个简单的回顾
- `react-redux`自动帮我们监测`redux`中的状态变化
- `react-redux`提供了`Provider`组件来替我们批量传递`store`
以上便是我们上节课的主要要点,接下来,我们来开始这节课的内容。
## 组件结构
我们先来回顾一下我们目前的项目中的文件结构是什么样子?
- `Sum`组件
- `Sum UI`组件
- `Sum`容器组件
- `redux`
是不是这样?那么如果我们要上整个项目中有几十个组件要和`redux`交互,那么我们这不就相当于组件数要翻一倍?因为每个组件都要分开成容器组件和`UI`组件啊。那么我们有没有什么办法给整合一下呢?
那么我们把这俩组件写在同一个文件里是不是就可以了?
我们来看容器组件中都干了什么?
```jsx
import SumUI from '../../components/Sum';
export default connect(
state => ({ sum: state }),
{
increment: createIncrementAction,
decrement: createDecrementAction,
asyncIncrement: createAsyncIncrementAction
}
)(SumUI);
```
我们是不是默认暴露了一个容器组件?这个容器组价里面是不是要用到`UI`组件?那么`UI`组件是哪来的?是不是我们手动引入的?除了在容器组件中我们有用到过`UI`组件吗?是不是没有?所有的对外沟通都是通过容器组件来完成的?那么我们把`UI`组件和容器组件都写在同一个文件里的话,是不是不需要暴露`UI`组件了?那么我们改一下
```jsx
import React, { Component } from 'react'
...
class Sum extends Component {...}
export default connect(
state => ({ sum: state }),
{
increment: createIncrementAction,
decrement: createDecrementAction,
asyncIncrement: createAsyncIncrementAction
}
)(Sum);
```
我们来看一下,这样的话我们直接在这里定义`UI`组件,然后再容器组件中直接拿来用是不是就可以了?
## 优化总结
- `UI`组件和容器组件混在一个文件中来编写
- 使用`Provider`组件来批量给容器组件传递`store`
- `react-redux`自动监测`redux`的状态改变
- `mapDispatchToProps`可以简写车给一个对象
- 不再暴露`UI`组件

67
day29/note/lesson109_share_data_person_component.md

@ -0,0 +1,67 @@
# 数据共享编写`Person`组件
上节课我们完成了最后一项优化,这节课我们开始介绍如何通`redux`来完成数据共享。
## 回顾
在开始这节课的内容之前,我们来对上节课的内容来做一个简单那回顾
- `UI`组件和容器组件混在一个文件中来编写
- 使用`Provider`组件来批量给容器组件传递`store`
- `react-redux`自动监测`redux`的状态改变
- `mapDispatchToProps`可以简写车给一个对象
- 不再暴露`UI`组件
以上便是我们上节课的主要要点,接下来,我们来开始这节课的内容。
## `Person`组件
我们之前的案例中是不是只有一个组件啊?其实我们之前的案例中早就可以实现数据共享了,但是就只有一个组件怎么共享呢,那么我们就再加一个组件。
那么在我定义`Preson`组件之前,我们是不是要创建两个文件?一个是`person_action_creator.js`另一个是`person_reducer.js`?因为`Person`组件要和`redux`打交道肯定要有自己的`reducer`和自己的`action creator`,那么我们把文件先建好。
但是还有一个问题,我们这么写到后来文件是不是就越来越乱啊?那么我们来建两个文件夹叫`actions`和`reducers`,然后我们把这些文件放进去,而且也就不需要加后缀了,我们就叫`person.js`。
然后我们定义组件,先定义一个`UI`组件,我们来看一下效果:
![image-20220119153120669](https://file.lynchow.com/2022-01-19-073123.png)
我们的`Person`组件是不是就可以成功渲染了?那么我们来定义容器组件能够让`Person`组件拿到`redux`中的值。
## 需求
现在嫩我们来做一个需求,我们要在`Person`组件中输入相关信息,然后点击按钮,就让`Sum`组件来展示`Person`信息,然后我们在`Sum`组件中点击这些按钮,就让`Person`组件来展示`Sum`中的求和之后的和。那么我们怎么处理?是不是要把`Person`的数据和`Sum`的数据都放在`redux`里面啊?
## 定义`Person`组件
我们知道我们的具体需求了之后,那么我们先来把`Person`的基础功能先完成:
```jsx
export default class Person extends Component {
render() {
return (
<div>
<input ref={c=>this.name=c} type="text" placeholder="input name" />
<input ref={c=>this.age=c} type="text" placeholder="input age" />
<button onClick={this.addPerson}>submit</button>
<h2>当前求和为:???</h2>
</div>
)
}
addPerson = ()=>{
const name = this.name.value;
const age = this.age.value;
console.log(name,age);
}
}
```
我们来看一下,这是最基本的组件页面元素,我们看一下我们的`onClick`事件的回调,我们通过`ref`获取了输入的值,然后打印出来,因为我们还没有定义操作`redux`的相关内容。那么就看能不能实现打印
![image-20220119161012129](https://file.lynchow.com/2022-01-19-081013.png)
我们现在是可以打印出来的。接下来我们再添加交互是不是就可以了。
## 总结
本节为准备工作,没有什么重要知识点。

41
day29/react-sum/package.json

@ -0,0 +1,41 @@
{
"name": "react-sum",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.6",
"react-scripts": "5.0.0",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"web-vitals": "^2.1.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

11
day29/react-sum/public/index.html

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description" content="Web site created using create-react-app"/>
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

14
day29/react-sum/src/App.js

@ -0,0 +1,14 @@
import React, { Component } from 'react'
import Sum from './containers/Sum'
import Person from './containers/Person'
export default class App extends Component {
render() {
return (
<div>
<Sum />
<Person />
</div>
)
}
}

19
day29/react-sum/src/containers/Person/index.jsx

@ -0,0 +1,19 @@
import React, { Component } from 'react'
export default class Person extends Component {
render() {
return (
<div>
<input ref={c=>this.name=c} type="text" placeholder="input name" />
<input ref={c=>this.age=c} type="text" placeholder="input age" />
<button onClick={this.addPerson}>submit</button>
<h2>当前求和为:???</h2>
</div>
)
}
addPerson = ()=>{
const name = this.name.value;
const age = this.age.value;
console.log(name,age);
}
}

51
day29/react-sum/src/containers/Sum/index.jsx

@ -0,0 +1,51 @@
import React, { Component } from 'react'
import { connect } from 'react-redux';
import {
createIncrementAction,
createDecrementAction,
createAsyncIncrementAction
} from '../../redux/actions/sum';
class Sum extends Component {
render() {
return (
<div>
<select ref={c => this.selectNum = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.dec}>-</button>
<button onClick={this.odd}>odd</button>
<button onClick={this.async}>async</button>
<h2>输入的信息为????</h2>
</div>
)
}
add = () => {
const { value } = this.selectNum;
this.props.increment(value * 1)
}
dec = () => {
const { value } = this.selectNum;
this.props.decrement(value * 1)
}
odd = () => {
const { value } = this.selectNum;
if (this.props.sum % 2 !== 0) this.props.increment(value * 1)
}
async = () => {
const { value } = this.selectNum;
this.props.asyncIncrement(value * 1, 500);
}
}
export default connect(
state => ({ sum: state }),
{
increment: createIncrementAction,
decrement: createDecrementAction,
asyncIncrement: createAsyncIncrementAction
}
)(Sum);

12
day29/react-sum/src/index.js

@ -0,0 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);

0
day29/react-sum/src/redux/actions/person.js

9
day29/react-sum/src/redux/actions/sum.js

@ -0,0 +1,9 @@
import { INCREMENT, DECREMENT } from "../constant"
export const createIncrementAction = data => ({ type: INCREMENT, data });
export const createDecrementAction = data => ({ type: DECREMENT, data });
export const createAsyncIncrementAction = (data, time) => {
return dispatch => setTimeout(() => {
dispatch(createIncrementAction(data))
}, time);
}

2
day29/react-sum/src/redux/constant.js

@ -0,0 +1,2 @@
export const INCREMENT = "increment";
export const DECREMENT = "decrement";

0
day29/react-sum/src/redux/reducers/person.js

14
day29/react-sum/src/redux/reducers/sum.js

@ -0,0 +1,14 @@
import { INCREMENT, DECREMENT } from '../constant';
export default function sumReducer(preState, action) {
if (preState === undefined) preState = 0;
const { type, data } = action;
switch (type) {
case INCREMENT:
return preState + data;
case DECREMENT:
return preState - data;
default:
return preState;
}
}

5
day29/react-sum/src/redux/store.js

@ -0,0 +1,5 @@
import { createStore,applyMiddleware } from 'redux';
import sumReducer from './reducers/sum';
import thunk from 'redux-thunk';
export default createStore(sumReducer,applyMiddleware(thunk));

8699
day29/react-sum/yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save