UI组件和容器组件
- 我们让 UI组件 负责页面的渲染,让 容器组件 负责页面的逻辑
- 当然有时候让 UI组件 负责一点点逻辑也是可以的。
- 当 UI组件 只负责页面渲染时,它实际只有一个
render
函数,那么此时我们可以使用无状态组件代替它以提高性能。
分离UI组件TodoListUI
在src下新建TodoListUI.js
文件,将**TodoList.js
的render函数内页面渲染相关的内容都剪切过去,放在组件TodoListUI中。将父组件TodoList.js
中有关页面渲染的引用**都剪切到子组件TodoListUI.js
中进行引用:
在 父组件TodoList.js
的render函数中调用 子组件TodoListUI
,并将子组件需要的**state
相关数据和父组件方法都通过子组件属性**的方式传递过去 ,页面的渲染就交给子组件:
构造函数中:this.handleItemDelete = this.handleItemDelete.bind(this);
1 | render() { |
注意:子组件事件绑定函数传参
原本父组件中有一个事件绑定函数handleItemDelete在bind()绑定this指向的同时进行了传参,现在改为在父组件中bind()绑定this指向,在子组件中传参:
原本的:
在React这个例子中使用bind绑定事件函数 handleItemDelete 的this指向时还要将参数index传入函数,那么我们可以在绑定时传入:
1 | <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item> |
现在:
但如果事件函数和bind绑定都在父组件中,而函数的调用和传参在子组件,就可以这样传参:
- 在父组件的构造函数中使用bind绑定
this.handleItemDelete=this.handleItemDelete.bind();
- 我们知道bind()返回的还是一个函数,所以在子组件中,我们通过箭头函数在每次调用handleItemDelete()时传入参数index:注意:
1
<List.Item onClick={()=>{this.props.handleItemDelete(index)}}>{item}</List.Item>
- 在这里不能直接使用
handleItemDelete(index)
传参是因为handleItemDelete(index)
直接执行了函数,而onClick
需要绑定的事件函数是不能立即执行的,所以需要新建一个箭头函数让他不立即执行。 - **箭头函数是JS表达式,在JSX中需要使用
{}
**,调用的父组件的函数也是JS表达式,同样中需要使用{}
。
容器组件(父组件TodoList
)
那么分离以后的父组件TodoList
也就成为了容器组件
无状态组件(性能优化)
- 当一个组件中只有render函数时,我们可以使用 无状态组件 替换 它。
- 无状态组件:使用常量
const
定义一个箭头函数,该函数接收一个参数props
(来自父组件的变量、方法),返回原本render函数中的内容(记得输出)
好处
相对普通组件,无状态组件性能高:
因为无状态组件实际上只是一个函数,而普通组件是JS的class(类),类生成的对象里还会有一些 生命周期函数,所以普通组件既要执行生命周期函数又要执行render函数,性能就比不上 无状态组件。
例子:TodoListUI
组件
将TodoListUI
组件替换成TodoListUI
组件:
使用常量const
定义无状态组件,它是一个函数,接收一个参数props
(来自父组件的变量、方法),返回原本render函数中的内容。(此时原本使用this.props.xx
来调用的父组件变量/方法 改为 使用props.xx
来调用):
复习:第三方模块axios(发送AJAX)
在React高级内容(3)中我们提到,React并不像jquery那样封装了AJAX发送的内置功能,所以我们需要借助 第三方模块axios来使用AJAX。
Redux 中发送异步请求获取数据
- 实现功能:我们希望发送一个AJAX请求来获取数据放入列表中。
- 其实在React高级内容(3)中我们实现过同样的功能,唯一的不同是现在我们通过
redux
的store
管理state
数据,所以我们需要走redux
的工作流程进行数据修改。
总流程
先“从接口获取到数据的流程”,然后将获取到的数据通过“redux工作流程”显示在页面上。
从接口获取到数据的流程
- 在组件中创建一个生命周期函数
componentDidMount()
。引用axios
模块,在componentDidMount函数中借助axios
模块发送ajax
请求,使用axios.get("接口路径")
来获取某个接口路径下的数据,请求成功后执行then()
的回调函数(接收一个参数res
表示接收到的数据),请求失败后执行catch()
的回调函数 - 桌面创建
list.json
文件,并在其中放入一个数组 - 使用接口地址在**
Charles
中模拟接口数据,使得发送请求到接口路径时调用我们桌面的list.json
文件【使用Charles抓取localhost包需要注意更换url:需要使用http://localhost.charlesproxy.com:3000/
访问**】 then()
的回调函数中打印出的res
中的内容,可得到res
中的data
是我们需要的数组,那么我们可以在ajax
请求成功时通过then()
获取data
数据
redux工作流程
- 在
actionTypes.js
中创建type
,然后在actionCreator.js
中,创建action
方法 - 在组件
TodoList.js
中,引用该方法**获取action
,并将从接口获取到的数据data
传入action
**,并通过store.dispatch(action)
将action
发送给store
store
自动将action
和state
传给reducer.js
reducer.js
根据action
的type
处理相关action
后返回newState
给store
store
拿到newState
后自动将其替换掉自己原本的state
- 组件通过
store.subscirbe(this.handleStoreChange)
监听到store
的数据变化时执行回调函数this.handleStoreChange
进行自身state
数据的更新,页面重新渲染
实例
1.在组件中创建一个生命周期函数componentDidMount()
。引用 axios
模块,在componentDidMount函数中借助 axios
模块发送ajax
请求,使用axios.get("接口路径")
来获取某个接口路径下的数据,请求成功后执行then()
的回调函数(接收一个参数res
表示接收到的数据),请求失败后执行catch()
的回调函数:
1 | import axios from "axios"; |
1 | // 生命周期函数 |
2.桌面创建list.json
文件,并在其中放入一个数组:
1 | ["你好呀","可以通过点击删除我","今天也要加油哦"] |
3.使用接口地址在**Charles
中模拟接口数据,使得发送请求到接口路径时调用我们桌面的list.json
文件【使用Charles抓取localhost包需要注意更换url】,使用http://localhost.charlesproxy.com:3000/
访问**:
4.then()
的回调函数中打印出的res
中的内容,可得到 res
中的data
是我们需要的数组,那么我们可以在ajax
请求成功时通过then()
获取data
数据:
1 | // 生命周期函数 |
5.在actionTypes.js
中创建type
,然后在actionCreator.js
中,创建action
方法:
1 | export const INIT_LIST_ACTION="init_list_action"; |
1 | export const initListAction = (data) => ({ |
6.在组件TodoList.js
中,引用该方法**获取action
,并将从接口获取到的数据data
传入action
**,并通过store.dispatch(action)
将action
发送给store
:
1 | // 生命周期函数 |
7.store
自动将action
和state
传给reducer.js
8.reducer.js
根据action
的type
处理相关action
后返回newState
给store
:
1 | if (action.type === INIT_LIST_ACTION) { |
【reducer中list没拿到数据】
9.store
拿到newState
后自动将其替换掉自己原本的state
10.组件通过store.subscirbe(this.handleStoreChange)
监听到store
的数据变化时执行回调函数this.handleStoreChange
进行自身state
数据的更新,页面重新渲染: