React 三个重要概念:Ref、Props、State 详解
在 React 中,props、state 和 ref 都非常重要。
可以简单理解为:
| 名称 | 作用 | 修改后是否重新渲染 |
|---|---|---|
props |
父组件传递给子组件的数据 | 通常会 |
state |
组件内部管理的数据 | 会 |
ref |
获取 DOM 或保存可变数据 | 不会 |
一、props:组件之间传递数据
props 是父组件传递给子组件的数据。
1. 基本使用
jsx
function Parent() {
return <Child name="小明" age={18} />
}
function Child(props) {
return (
<div>
姓名:{props.name}
<br />
年龄:{props.age}
</div>
)
}
也可以直接解构:
jsx
function Child({ name, age }) {
return (
<div>
姓名:{name}
<br />
年龄:{age}
</div>
)
}
2. 传递不同类型的数据
jsx
function Parent() {
const user = {
name: '小明',
age: 18
}
return (
<Child
name="小明"
age={18}
isStudent={true}
user={user}
/>
)
}
注意:
jsx
name="小明"
传递的是字符串。
jsx
age={18}
isStudent={true}
user={user}
花括号中可以传递数字、布尔值、变量、对象、数组或函数。
3. props 是只读的
子组件不应该直接修改 props:
jsx
function Child(props) {
// 不推荐这样做
props.name = '小红'
return <div>{props.name}</div>
}
props 应该由父组件负责修改。如果子组件想通知父组件,可以通过回调函数:
jsx
import { useState } from 'react'
function Parent() {
const [message, setMessage] = useState('')
return (
<div>
<p>{message}</p>
<Child onSend={setMessage} />
</div>
)
}
function Child({ onSend }) {
return (
<button onClick={() => onSend('子组件传来的消息')}>
发送消息
</button>
)
}
4. children 属性
组件标签中嵌套的内容,会通过 children 属性传递:
jsx
function App() {
return (
<Card>
<p>这是卡片内容</p>
</Card>
)
}
function Card({ children }) {
return <div className="card">{children}</div>
}
二、state:组件内部的数据
state 是组件内部自己管理的数据。
当 state 发生变化时,React 会重新渲染组件。
1. 基本使用
jsx
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
function handleClick() {
setCount(count + 1)
}
return (
<button onClick={handleClick}>
点击次数:{count}
</button>
)
}
这段代码中:
jsx
const [count, setCount] = useState(0)
表示:
count:当前状态值setCount:修改状态的方法0:状态初始值
2. 不要直接修改 state
错误写法:
jsx
count = count + 1
正确写法:
jsx
setCount(count + 1)
修改对象或数组时,也要创建新的对象或数组:
jsx
const [user, setUser] = useState({
name: '小明',
age: 18
})
setUser({
...user,
age: 19
})
修改数组:
jsx
const [list, setList] = useState([])
setList([
...list,
'新数据'
])
3. 根据旧值修改 state
如果新状态依赖旧状态,推荐使用函数形式:
jsx
setCount(prevCount => prevCount + 1)
例如:
jsx
function handleClick() {
setCount(prev => prev + 1)
setCount(prev => prev + 1)
}
这样可以确保状态基于最新值进行更新。
4. state 的特点
state 通常用于保存:
- 输入框的内容
- 计数器的数字
- 是否显示弹窗
- 当前选中的菜单
- 商品数量
- 加载状态
- 列表数据
只要数据变化后需要更新页面,就应该考虑使用 state。
三、ref:获取 DOM 或保存可变数据
ref 主要有两个用途:
- 获取 DOM 元素
- 保存一个变化后不需要重新渲染页面的数据
1. 获取 DOM 元素
jsx
import { useRef } from 'react'
function App() {
const inputRef = useRef(null)
function handleFocus() {
inputRef.current.focus()
}
return (
<div>
<input ref={inputRef} />
<button onClick={handleFocus}>
聚焦输入框
</button>
</div>
)
}
执行过程:
useRef(null)创建一个 ref 对象。ref={inputRef}绑定到输入框。- 输入框 DOM 元素会保存到
inputRef.current。 - 调用
inputRef.current.focus()让输入框获得焦点。
2. ref 保存可变数据
jsx
import { useRef } from 'react'
function App() {
const countRef = useRef(0)
function handleClick() {
countRef.current += 1
console.log(countRef.current)
}
return <button onClick={handleClick}>点击</button>
}
修改 countRef.current 不会触发组件重新渲染。
3. State 和 Ref 的区别
如果数据变化后需要显示到页面上,使用 state:
jsx
const [count, setCount] = useState(0)
如果数据变化后不需要更新页面,使用 ref:
jsx
const countRef = useRef(0)
例如:
- 页面上的计数器:使用
state - 保存定时器 ID:使用
ref - 输入框自动聚焦:使用
ref - 保存上一次的值:可以使用
ref
四、props、state 和 ref 的区别
Props
jsx
<Child name="小明" />
特点:
- 来自父组件
- 子组件只能读取,不能直接修改
- 用于组件之间传递数据
State
jsx
const [count, setCount] = useState(0)
特点:
- 由组件自己管理
- 使用 setter 修改
- 修改后会触发重新渲染
- 用于保存页面需要显示的数据
Ref
jsx
const inputRef = useRef(null)
特点:
- 可以获取 DOM 元素
- 可以保存可变数据
- 修改
.current不会触发重新渲染 - 适合操作 DOM、保存定时器等数据
五、实际开发中如何选择
可以按照下面的方式判断:
text
父组件传给子组件的数据 使用 props
组件内部需要显示的数据 使用 state
需要操作 DOM 元素 使用 ref
数据变化但不需要刷新页面 使用 ref
例如:
jsx
function App() {
const inputRef = useRef(null)
const [text, setText] = useState('')
return (
<div>
<input
ref={inputRef}
value={text}
onChange={event => setText(event.target.value)}
/>
<p>输入内容:{text}</p>
<button onClick={() => inputRef.current.focus()}>
聚焦输入框
</button>
</div>
)
}
这段代码中:
text是state,因为它需要显示在页面上。inputRef是ref,因为它用于操作输入框 DOM。- 如果把数据传给子组件,就会使用
props。
六、总结
React 中的三个重要概念可以这样记忆:
text
props:父组件传给我的数据
state:我自己管理,并且会影响页面的数据
ref:我想直接操作 DOM 或保存数据,但不想触发重新渲染
最简单的判断方法是:
页面要不要随着数据变化而更新?
如果需要,使用 state;如果不需要,可以考虑使用 ref。