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.jsreducer.js根据action的type处理相关action后返回newState给storestore拿到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数据的更新,页面重新渲染: