props
1. 使用
React主要使用props来互相通信,props有点类似于vue中的父子组件传值,实际上React也是类似的。
因为React的特性就是具有单向数据流,所以主要作用就是父组件向子组件中传递数据。
React核心思想是组件化思想,页面被切分成独立、可复用组件。组件从概念上看就是一个函数,既然是函数,就需要接受参数。而这个参数就是props,props理解为从外部传入组件内部的数据。
props不仅可以传字符串,数字,还可以传递对象,数组和回调函数。举例如下
类组件:
class Welcome extends React.Component {
render() {
return <h1>Hello {this.props.name}</h1>
}
}
const element = <Welcome name="Jack" onNameChanged={this.handleName} />
上述name属性和onNameChanged方法都能在子组件的props中访问。
函数式组件:
import { getUrl } from './utils.js';
function Example({ person, size }) {
return (
<img
className="example"
src={getUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function ExportExam() {
return (
<div>
<Example
size={100}
person={{
name: 'Lily',
imageId: '88dsfaddsf'
}}
/>
</div>
<Example
size={50}
person={{
name: 'Jack',
imageId: '000dfdsa'
}}
/>
</div>
);
}
刚才提到,props类似于向函数中传递的参数,向person和size props渲染的Example中添加一些数据,就能通过不同的props,多次以不同方式渲染。改变person和size,无需考虑Example如何使用这些数据。
2. 解构
!注意:function Example({ person, size }) 中的括号不能忘记,这种语法被称为“解构”,等价于从函数参数中读取属性。
function Example({ person, size }){
// ...
} 等价于
function Example(props) {
let person = props.person;
let size = props.size;
}
3. 指定默认值
如果想给prop指定默认值,可以在参数后面指定,默认值仅在缺少size prop时或size={undefined}时生效,如果传递了size={null}或size={0},默认值将不被使用,如:
function Example({ person, size = 100}){
// ...
}
注意:单向数据流的特点之一就是,props在内部不可变,想改变只能通过外部组件传入新的props来重新渲染子组件,否则子组件的props和展示形式不变。实际上这也是为React带来安全性的特点之一,props相关数据不能在内部随意变更。
总结:
- 1.可以通过解构读取props,记得用{}包裹解构的变量
- 2.可以指定默认值用于缺少值或者值为undefined的情况的props
- 3.可以使用<Example {...props}/>JSX语法展开props,但不要过度使用
- 4.props是不可变的单向数据流,需要交互和改变的时候需要用到state。
state
1.state的作用
一个组件的显示形态可以由数据状态和外部参数决定,外部参数就是上面提到的props,而数据状态就是state,一般在constructor中初始化。 之前说到props的值是不可变的,需要交互和改变的时候要用到state,那么state如何实现值的变化呢?
export default function Example() {
let index = 0;
function handleClick() {
index = index + 1;
}
return (
<>
<button onClick={handleClick}>
Next
</button>
<div>{index}<div/>
</>
);
}
点击按钮时,handleClcik应该将index加1,然而变化并未生效。有两个原因: 1.局部变量无法在多次渲染中持久保存。React重新渲染组件时会从头渲染,不考虑之前对局部变量的更改。 2.更改局部变量并没有触发渲染,即React没有意识到要重新渲染。
所以,需要用新的数据渲染组件,需要对应两个变化:
1.要么保存变化的数据。
2.要么触发React用新数据重新渲染。
而useState作为React的Hook正是提供了这两种功能:
1.用State变量保存变化数据。
2.State setter函数用于更新变量并触发重新渲染。
举例:
//从文件中引入该Hook
import {useState} from 'react'
export default function Example() {
//let index = 0;
//声明state变量
const [index,setIndex] = useState(0);
function handleClick() {
//更新index,并触发重新渲染
setIndex(index + 1);
}
return (
<>
<button onClick={handleClick}>
Next
</button>
<div>{index}<div/>
</>
);
}
注意:useState等其他以“use”开头的函数都被称为Hook。Hook是一种特殊的函数,只在React渲染的时候生效。Hooks这些以use开头的函数只能在组件或自定义Hook的最顶层调用。不能在条件语句,循环语句或其他嵌套函数内调用Hook,Hook是函数,在顶部使用,类似于在文件顶部导入模块。
2.本质
- 组件第一次渲染,将0作为index的初始值传给useState,返回的是[0,setIndex].
- 更新state时,调用setIndex(index+1)。index是0,所以setIndex(1),告诉React现在记住index是1并触发下一次渲染。
- 组件第二次渲染。React仍然看到useState(0),但是React记住了index变成了1,所以返回[1,setIndex]。
- 以此类推。
3.state是隔离且私有的
当渲染一个组件两次,每个副本都会有完全隔离的state,改变其中一个不会影响另一个。
//从文件中引入该Hook
import {useState} from 'react'
export default function Example() {
//let index = 0;
//声明state变量
const [index,setIndex] = useState(0);
function handleClick() {
//更新index,并触发重新渲染
setIndex(index + 1);
}
return (
<>
<button onClick={handleClick}>
Next
</button>
<div>{index}<div/>
<button onClick={handleClick}>
Next
</button>
<div>{index}<div/>
</>
);
}
渲染了两个按钮点击变化显示值的代码,它们的index是分别存储的,不会互相影响。如果想要两个index同步的话,从子组件中删除,添加到离它们最近的父组件中进行状态提升。
4.组件间共享state
我们知道state的特性是组件私有的,两个地方渲染它,每个副本拥有自己的state,互相独立。
所以要做到共享state,需要进行状态提升,将状态提升到它们的父组件中。
-
- 从子组件中移除state
-
- 从父组件中传递硬编码数据。
-
- 为共同的父组件添加state,将其与时间处理函数一起向下传递。 如下所示:
//从文件中引入该Hook
import {useState} from 'react'
export default function Example() {
//let index = 0;
//声明state变量
const [index,setIndex] = useState(0);
function handleClick() {
//更新index,并触发重新渲染
setIndex(index + 1);
}
return (
<>
<Share index={index} />
</>
);
}
function Share({index}) {
return (
<>
<button onClick={handleClick}>
Next
</button>
<div>{index}<div/>
<button onClick={handleClick}>
Next
</button>
<div>{index}<div/>
</>
);
}
注:如果想要整合两个组件,将state移动到共同的父组件中,在父组件中通过props把信息传递下去。向下传递事件。当组件由prop驱动,则称为受控组件;由state驱动称为不受控组件。
总结
- 如果一个组件需要在多次渲染期间记住某些信息时使用state变量。
- state变量是通过调用useState Hook来声明的。
- Hook是以use开头的特殊的函数,该函数仅仅在组件或另一个Hook的顶层调用才有效,不能在条件语句,循环语句或其他嵌套函数内调用Hook。
- state是组件私有的,两个地方渲染它,每个副本拥有自己的state,互相独立。
prop和state对比
相同点:
- 两者都是JavaScript对象。
- 两者都是用于保存信息。
- props和state都能触发渲染更新。
区别:
- props是外部传递给组件的,state是在组件内被组件自己管理的。
- props在组件内部不可修改,state在组件内部可以进行修改。
- props是组件内公有的,state是组件私有的,两个地方渲染它,每个副本拥有自己的state,互相独立。要想让state变成组件公有的,需要进行状态提升,将状态提升到父组件。