组件

函数组件

1
2
3
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

“函数组件”本质上就是 JavaScript 函数

class组件

1
2
3
4
5
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

渲染组件

当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。

例如,这段代码会在页面上渲染 “Hello, Sara”:

1
2
3
4
5
6
7
function Welcome(props) {  
return <h1>Hello, {props.name}</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render(element);

回顾一下这个例子中发生了什么:

  • 调用root.render()函数,并传入 <Welcome name="Sara" /> 作为参数。
  • React 调用 Welcome 组件,并将 {name: 'Sara'} 作为props传入。
  • Welcome 组件将 <h1>Hello, Sara</h1> 元素作为返回值。
  • React DOM 将 DOM 高效地更新为 <h1>Hello, Sara</h1>

注意: 组件名称必须以大写字母开头!!!
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div /> 代表 HTML 的 div 标签,而 <Welcome /> 则代表一个组件,并且需在作用域内使用 Welcome

组合组件

组件可以在其输出中引用其他组件

1
2
3
4
5
6
7
8
9
10
11
12
13
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}

提取组件

将组件拆分为更小的组件。

参考如下Comment组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}

该组件由于嵌套的关系,变得难以维护,且很难复用它的各个部分。因此,让我们从中提取一些组件出来。

首先,我们将提取 Avatar 组件:

1
2
3
4
5
6
7
8
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}

Avatar 不需知道它在 Comment 组件内部是如何渲染的。因此,我们给它的 props 起了一个更通用的名字:user,而不是 author

我们建议从组件自身的角度命名 props,而不是依赖于调用组件的上下文命名。

我们现在针对 Comment 做些微小调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}

接下来,将提取 UserInfo 组件,该组件在用户名旁渲染 Avatar 组件:

1
2
3
4
5
6
7
8
9
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div> );
}

进一步简化 Comment 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}

小总结:提取组件可能繁重,但值得

Props 的只读性

组件无论是使用函数声明还是通过 class 声明,都绝不能修改自身的 props

React 非常灵活,但它也有一个严格的规则:

所有 React 组件都必须像纯函数一样保护它们的 props 不被更改

组件通讯

父组件传递数据给子组件

  • 1.父组件提供要传递的state数据
  • 2.给子组件标签添加属性,值为state中的数据
  • 3.子组件中通过props接收父组件中传递的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Parent extends React.Component{
state ={lastName:'王'}
render(){
return(
<div>
传递数据给子组件:<Child name={this.state.lastName}/>
</div>
)
}
}

function Child(props){
return<div>子组件接收到数据:{props.name}</div>
}

子组件传递数据给父组件

思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。

  • 1.父组件提供一个回调函数(用于接收数据)
  • 2.将该函数作为属性的值,传递给子组件
1
2
3
4
5
6
7
8
9
10
11
12
class Parent extends React.Component{
getchildMsg = (msg)=>{
console.log('接收到子组件数据',msg)
}
render(){
return(
<div>
子组件:<Child getMsg={this.getchildMsg}/>
</div>
)
}
}
  • 3.子组件通过props调用回调函数
1
2
3
4
5
6
7
8
9
10
11
class Child extends React.Component{
state ={childMsg:'React'}
handleClick =()=>{
this.props.getMsg(this.state.childMsg)
}
render(){
return(
<button onClick={this.handleClick}>点我,给父组件传递数据</button>
)
}
}

兄弟组件

  • 将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
  • 思想:状态提升
  • 公共父组件职责:
    • 1.提供共享状态
    • 2.提供操作共享状态的方法
  • 要通讯的子组件只需通过pops接收状态或操作状态的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Counter extends React.Component{
//提供共享状态
state={
count:0
}
//提供修改状态的方法
onIncrement =()=>{
this.setstate({
count:this.state.count + 1
})
}
render(){
return(
<div>
<Child1 count={this.state.count} />
<Child2 onIncrement={this.onIncrement} />
</div>
)
}
}

const child1=props =>{
return<h1>计数器:{props.count}</h1>
}
const child2=props =>{
return <button onClick={()=>props.onIncrement()}>+1</button>
}