1. 组件通讯-概念
了解组件通讯的意义
大致步骤:
- 知道组件的特点
- 知道组件通讯意义
具体内容:
- 组件的特点
- 组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据
- 在组件化过程中,通常会将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能
- 知道组件通讯意义
- 而在这个过程中,多个组件之间不可避免的要共享某些数据
- 为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通
- 这个过程就是组件通讯
总结:
具体内容:
- 传递数据和接收数据的过程
- 使用组件的时候通过属性绑定数据,在组件内部通过 props 获取即可。
- 函数组件使用 props
// 使用组件
// 定义组件 props包含{name:’jack’,age:’20’}
function Hello(props) {
return
}
- 类组件使用 props
// 使用组件
// 定义组件 props包含{name:’jack’,age:’20’}
class Hello extends Component {
render() {
return
}
}
总结:
props 是实现组件通讯的关键,它通过使用组件绑定属性,组件内部使用 props 来传值。
3. 组件通讯-props 注意事项
知道 props 是单项数据流只读,但是可以传递任意数据。
大致步骤:知道什么是单向数据流
- 知道 props 可以传递什么数据
具体内容:
- 知道什么是单向数据流?
- 单向数据流,是从上到下的,自顶而下的,数据流。
- 好比:河流,瀑布,只能从上往下流动,上游污染下游受影响,但是下游不能影响上游。
- 父组件传递数据给子组件,父组件更新数据子组件自动接收更新后数据,当是子组件是不能修改数据的。
- props 可以传递什么数据?任意
- 字符串
- 数字
- 布尔
- 数组
- 对象
- 函数
- JSX (插槽)
总结:
-
4. 组件通讯-父传子方式
通过 props 将父组件的数据传递给子组件
大致步骤: 父组件提供要传递的 state 数据
- 给子组件标签添加属性,值为 state 中的数据
- 子组件中通过 props 接收父组件中传递的数据
具体代码:
- 父组件提供要传递的 state 数据
class Parent extends React.Component {
state = {
money: 10000,
};
render() {
return (
父组件:{this.state.money}
);
}
}
- 给子组件标签添加属性,值为 state 中的数据
class Parent extends React.Component {
state = {
money: 10000
}
render() {
return (
父组件:{this.state.money}
+
)
}
}
- 子组件中通过 props 接收父组件中传递的数据
function Child(props) {
return (
子组件:{props.money}
);
}
总结:
- jsx 结构
今日要闻
坚定不移走中国特色社会主义法治道路
新华社
774点赞
2021年度法治人物——倪伯苍
央视网
774点赞
岁末年终 愿这份“温良”伴你乘风破浪
央视新闻客户端
248点赞
- 组件样式
.parent {
width: 500px;
margin: 0 auto;
}
.parent h1 {
font-weight: normal;
margin: 0;
padding: 20px 0;
}
.child {
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}
.child h3 {
font-weight: normal;
color: #333;
margin: 0;
padding: 10px 0;
}
.child .detail {
font-size: 14px;
color: #999;
}
.child .detail span {
margin-right: 10px;
}
- 实现思路
- 父组件提供回调函数,通过 props 传递给子组件
- 子组件调用 props 中的回调函数,函数可传参
- 父组件函数的参数就是子组件传递的数据
具体代码:
- 父组件
class Parent extends React.Component {
state = {
money: 10000,
};
// 回调函数
buyPhone = (price) => {
this.setState({
money: this.state.money - price,
});
};
render() {
const { money } = this.state;
return (
父组件:{money}
);
}
}
- 子组件
const Child = (props) => {
const handleClick = () => {
// 子组件调用父组件传递过来的回调函数
props.buyPhone(5000);
};
return (
子组件:{props.money}
);
};
总结:
- 子组件如何传递数据给父组件?触发父组件传递的回调函数传入数据
- 父组件如何接收子组件的数据?回调函数的参数是子组件传递的数据
- 父组件数据更新后,传递给子组件的数据是否更新?自动更新
7. 组件通讯-子传父练习
通过提供的素材,利用子传父知识,完成点赞功能
界面参考:
配套素材:
- jsx 结构
坚定不移走中国特色社会主义法治道路
新华社
774点赞
+ 点赞+1
- 新增样式
.child .detail span:last-child {
cursor: pointer;
color: burlywood;
user-select: none;
}
- 实现思路
- 状态提升思想是什么?
- 演示通过状态提升完成兄弟组件通讯。
具体内容:
- 状态提升思想是什么?
- 将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态和修改状态的方法
- 需要通讯的组件通过 props 接收状态和函数即可
- 参考代码index.js
import React, { Component } from ‘react’;
import ReactDOM from ‘react-dom’;
// 导入两个子组件
import Jack from ‘./Jack’;
import Rose from ‘./Rose’;
// App 是父组件
class App extends Component {
// 1. 状态提升到父组件
state = {
msg: ‘’,
};
changeMsg = (msg) => {
this.setState({ msg });
};
render() {
return (
我是App组件
{/ 兄弟组件 1 /}
{/ 兄弟组件 2 /}
);
}
}
// 渲染组件
ReactDOM.render(
Jack.js
import React, { Component } from ‘react’;
export default class Jack extends Component {
say = () => {
// 修改数据
this.props.changeMsg(‘you jump i look’);
};
render() {
return (
我是Jack组件
);
}
}
Rose.jsx
import React, { Component } from ‘react’;
export default class Rose extends Component {
render() {
return (
我是Rose组件-{this.props.msg}
);
}
}
9. 组件通讯-context 跨级组件通讯
掌握使用 context 实现跨级组件通讯
大致步骤:
- 什么是跨级组件通讯?
- context 怎么去理解?
- 演示使用 context 完成跨级组件通讯。
具体内容:
- 什么是跨级组件通讯?
- 组件间相隔多层,理解成叔侄,甚至更远的亲戚。
- context 怎么去理解
- 术语:上下文
- 理解:一个范围,只要在这个范围内,就可以跨级组件通讯。(不需要 props 层层传递)
父组件:
子组件:10000 修改money
总结:
- 使用creatContext()创建一个上下文对象,包含:Provider Consumer 组件。
- 使用 Provider 包裹组件,value 属性注入状态,函数,被包裹组件下的任何组件可以使用。
使用 Consumer 消费 Provider 提供的数据和函数,语法{value=>使用数据和函数}
10. 评论案例-组件拆分
能够按照功能结构拆分组件组件
大致步骤:在 components 下分别创建 CommentInput CommentHead CommentList 三个组件
- 在 index.js 创建 App 组件,组织上面三个组件
- 准备 index.css 支持样式,导入到 index.js 即可
评论
热门评论(5)默认时间
- 清风徐来2012-12-12删除这里是评论的内容!!!这里是评论的内容!!!这里是评论的内容!!!
- 清风徐来2012-12-12这里是评论的内容!!!这里是评论的内容!!!这里是评论的内容!!!
字体图标:在 index.html 头部引入 https://at.alicdn.com/t/font_2998849_vtlo0vj7ryi.css
总结:
具体代码:
- 在 App 组件准备状态数据,传递给 CommentList 组件
state = {
// 用户信息
user: {
name: ‘清风徐来’,
avatar: ‘https://static.youku.com/lvip/img/avatar/310/6.png‘,
vip: true,
},
// 评论列表
comments: [
{
id: 100,
name: ‘__RichMan’,
avatar: ‘https://r1.ykimg.com/051000005BB36AF28B6EE4050F0E3BA6‘,
content:
‘这阵容我喜欢😍靳东&闫妮,就这俩名字,我就知道是良心剧集…锁了🔒’,
time: new Date(‘2021/10/12 10:10:23’),
vip: true,
collect: false,
},
{
id: 101,
name: ‘糖蜜甜筒颖’,
avatar:
‘https://image.9xsecndns.cn/image/uicon/712b2bbec5b58d6066aff202c9402abc3370674052733b.jpg‘,
content:
‘突围神仙阵容 人民的名义第三部来了 靳东陈晓闫妮秦岚等众多优秀演员实力派 守护人民的财产 再现国家企业发展历程’,
time: new Date(‘2021/09/23 15:12:44’),
vip: false,
collect: true,
},
{
id: 102,
name: ‘清风徐来’,
avatar: ‘https://static.youku.com/lvip/img/avatar/310/6.png‘,
content:
‘第一集看的有点费力,投入不了,闫妮不太适合啊,职场的人哪有那么多表情,一点职场的感觉都没有’,
time: new Date(‘2021/07/01 00:30:51’),
vip: true,
collect: false,
},
],
};
render() {
+ const {user, comments} = this.state
return (
{/ 输入框组件 /}
{/ 标题排序组件 /}
{/ 列表组件 /}
+
)
}
- 进行渲染,需要区分会员,控制删除按钮,展示收藏状态,格式化时间
- 格式化时间需要安装 dayjs
import dayjs from ‘dayjs’;
const CommentList = (props) => {
// 格式时间
const formatTime = (time) => dayjs(time).format(‘YYYY/MM/DD HH:mm:ss’);
return (
className=”avatar”
style={{ backgroundImage:url(${item.avatar})
}}
>
{item.name}
{item.vip && (
alt=””
src={
‘https://gw.alicdn.com/tfs/TB1c5JFbGSs3KVjSZPiXXcsiVXa-48-48.png‘
}
/>
)}
{formatTime(item.time)}
className={iconfont icon-collect${item.collect ? '-sel' : ''}
}
>
{item.name === props.user.name && (
删除
)}
{item.content}
{props.comments.map((item) => (
))}
);
};
export default CommentList;
总结:
- 状态数据都在 App 组件,其他组件都有共享的
格式化时间使用 dayjs 定义一个函数转换,复杂类名的控制大家可以尝试 classnames。
12. 评论案例-排序功能
根据选择的排序方式更新列表
大致步骤:在App组件定义当前排序方式数据,和修改数据的函数
- 传入 CommentHead 组件,点击排序的时候修改排序方式数据
- 在 CommentList 组件,根据列表数据和排序方式得到新的列表数据,渲染即可
具体代码:
- 在App组件定义当前排序方式数据,和修改数据的函数
state = {
// …省略
+ active: ‘default’
}
+ setActive = value => {
+ // 改变过排序修改状态
+ if (value !== this.state.active) {
+ this.setState({active:value})
+ }
+ }
- 传入 CommentHead 组件,点击排序的时候修改排序方式数据App组件
render() {
+ const {user, comments, active} = this.state
return (
{/ 输入框组件 /}
{/ 标题排序组件 /}
+
CommentHead组件
const CommentHead = (props) => {
return (
热门评论({props.comments.length})
onClick={() => props.setActive(‘default’)}
className={props.active === ‘default’ ? ‘active’ : ‘’}
>
默认
onClick={() => props.setActive(‘time’)}
className={props.active === ‘time’ ? ‘active’ : ‘’}
>
时间
);
};
export default CommentHead;
- 在 CommentList 组件,根据列表数据和排序方式得到新的列表数据,渲染即可App组件
{/ 列表组件 /}
+
CommentList组件
import dayjs from ‘dayjs’;
const CommentList = (props) => {
// 格式时间
const formatTime = (time) => dayjs(time).format(‘YYYY/MM/DD HH:mm:ss’);
+ // 得到排序后的数组
+ const newList = […props.comments];
+ if (props.active === ‘default’) {
+ newList.sort((p, c) => c.id - p.id);
+ }
+ if (props.active === ‘time’) {
+ newList.sort((p, c) => c.time - p.time);
+ }
return (
总结:- 先定义 active 数据 setActive 函数
- 点击排序修改 active
-
13. 评论案例-发表评论
通过子传父完成发表评论功能
大致步骤: 输入框变为受控组件,现在输入字数 100
- 点击发表评论,将内容传递给 App 组件,在 App 自己完成追加评论
具体代码:
- 输入框变为受控组件,现在输入字数 100
CommentInput组件
import React, { Component } from ‘react’;
export default class CommentInput extends Component {
state = {
content: ‘’,
};
setContent = (e) => {
const value = e.target.value.trim();
if (value.length <= 100) {
this.setState({ content: value });
}
};
render() {
return (
<>
评论
</>
);
}
}- 点击发表评论,将内容传递给 App 组件,在 App 自己完成追加评论CommentInput组件
- handleClick = () => {
+ this.props.addComment(this.state.content)
+ this.setState({content: ‘’})
+ }
render() {
return (
<>
评论
</>
);
}
APP组件
// 发表评论
addComment = (content) => {
// 递增ID
const id = Math.max(…this.state.comments.map((item) => item.id)) + 1;
const newComment = {
…this.state.user,
id,
content,
time: new Date(),
collect: false,
};
this.setState({
comments: […this.state.comments, newComment],
});
};
{/ 输入框组件 /}
+
总结:
CommentInput 组件把评论内容传递给 App 组件,然后组织完整的评论对象添加到数组
14. 评论案例-删除评论
通过子传父完成删除评论功能
大致步骤:App 组件提供删除的函数,根据 ID 删除数据,并且传递给 CommentList 组件
- CommentList 组件绑定删除按钮点击事件,调用props中的删除函数传入 ID
具体代码:
- App 组件提供删除的函数,根据 ID 删除数据,并且传递给 CommentList 组件
// 删除评论
delComment = (id) => {
this.setState({
comments: this.state.comments.filter((item) => item.id !== id),
});
};
{/ 列表组件 /}
user={user}
comments={comments}
active={active}
+ delComment={this.delComment}
/>- CommentList 组件绑定删除按钮点击事件,调用props中的删除函数传入 ID
props.delComment(item.id)} className=”del”>
删除
总结:-
15. 评论案例-收藏评论
通过子传父完成收藏评论功能
大致步骤: App 组件提供收藏函数,根据 ID 进行收藏&取消收藏,并且传递给 CommentList 组件
- CommentList 组件绑定收藏按钮点击事件,调用props中的收藏函数传入 ID
具体代码:
- App 组件提供收藏函数,根据 ID 进行收藏&取消收藏,并且传递给 CommentList 组件
// 收藏评论&取消收藏
collectComment = (id) => {
this.setState({
comments: this.state.comments.map((item) => {
if (item.id === id) {
return { …item, collect: !item.collect };
} else {
return item;
}
}),
});
};
{/ 列表组件 /}
user={user}
comments={comments}
active={active}
delComment={this.delComment}
+ collectComment={this.collectComment}
/>- CommentList 组件绑定收藏按钮点击事件,调用props中的收藏函数传入 ID
onClick={() => props.collectComment(item.id)}
className={iconfont icon-collect${item.collect ? '-sel' : ''}
}
>
总结:-
16. props-children 属性
掌握 props 中 children 属性的用法
大致步骤: props 中 children 属性代表什么?
- props 中 children 属性的使用
- 使用 props 中 children 属性 封装 NavBar 组件
具体内容:
- props 中 children 属性代表什么?
- 组件标签的子节点(标签之间的内容),可以是任意值(文本,React 元素,组件,函数)
- props 中 children 属性的使用
// 定义组件
const Hello = (props) => {
return该组件的子节点:{props.children};
};
// 使用组件我是子节点 ;17. 今日总结
- 组件状态特点?
- 相互独立的,互不影响。
- props 如何使用?
- 给组件标签上绑定属性,组件中 props 去使用。
- props 注意些什么?
- 传递的数据是单向的不能改,可以传递任意数据。
- 父传子如何实现?
- 父组件定义数据,给子组件标签绑定,子组件通过 props 获取
- 子传父如何实现?
- 父组件提供函数,给子组件标签绑定,子组件通过 props 调用且传参
- 兄弟组件通讯如何实现?
- 通过状态提升思想,数据和修改数据放在最近的父组件,传给兄弟组件使用和修改数据
- 跨级组件通讯如何实现?
- 通过createContext对象,提供注入数据的组件Provider,和消费数据的组件Consumer
- porps 的 children 作用?
- 获取组件标签之前的节点内容,任何类型。
+ {newList.map((item) => (