React添加交互篇
一、响应事件
在事件处理函数中读取 props
由于事件处理函数声明于组件内部,因此它们可以直接访问组件的 props。示例中的按钮,当点击时会弹出带有 message prop 的 alert:
javascript
function AlertButton({ message, children }) {
return (
<button onClick={() => alert(message)}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div>
<AlertButton message="正在播放!">
播放电影
</AlertButton>
<AlertButton message="正在上传!">
上传图片
</AlertButton>
</div>
);
}
将事件处理函数作为 props 传递
通常,我们会在父组件中定义子组件的事件处理函数。比如:置于不同位置的 Button 组件,可能最终执行的功能也不同 ------ 也许是播放电影,也许是上传图片。
为此,将组件从父组件接收的 prop 作为事件处理函数传递,如下所示:
javascript
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
function PlayButton({ movieName }) {
function handlePlayClick() {
alert(`正在播放 ${movieName}!`);
}
return (
<Button onClick={handlePlayClick}>
播放 "{movieName}"
</Button>
);
}
function UploadButton() {
return (
<Button onClick={() => alert('正在上传!')}>
上传图片
</Button>
);
}
export default function Toolbar() {
return (
<div>
<PlayButton movieName="魔女宅急便" />
<UploadButton />
</div>
);
}
示例中,Toolbar 组件渲染了一个 PlayButton 组件和 UploadButton 组件:
PlayButton将handlePlayClick作为onClickprop 传入Button组件内部。UploadButton将() => alert('正在上传!')作为onClickprop 传入Button组件内部。
最后,你的 Button 组件接收一个名为 onClick 的 prop。它直接将这个 prop 以 onClick={onClick} 方式传递给浏览器内置的 <button>。当点击按钮时,React 会调用传入的函数。
命名事件处理函数 prop
内置组件(<button> 和 <div>)仅支持 浏览器事件名称,例如 onClick。但是,当你构建自己的组件时,你可以按你个人喜好命名事件处理函数的 prop。
按照惯例,事件处理函数 props 应该以
on开头,后跟一个大写字母。
javascript
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('正在播放!')}
onUploadImage={() => alert('正在上传!')}
/>
);
}
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
播放电影
</Button>
<Button onClick={onUploadImage}>
上传图片
</Button>
</div>
);
}
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
二、state:组件的记忆
添加一个state变量
javascript
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img
src={sculpture.url}
alt={sculpture.alt}
/>
<p>
{sculpture.description}
</p>
</>
);
}
剖析 useState
当你调用 useState 时,你是在告诉 React 你想让这个组件记住一些东西:
scss
const [index, setIndex] = useState(0);
在这个例子里,你希望 React 记住 index。
惯例是将这对返回值命名为 const [thing, setThing]。你也可以将其命名为任何你喜欢的名称,但遵照约定俗成能使跨项目合作更易理解。
useState 的唯一参数是 state 变量的初始值 。在这个例子中,index 的初始值被useState(0)设置为 0。
每次你的组件渲染时,useState 都会给你一个包含两个值的数组:
- state 变量 (
index) 会保存上次渲染的值。 - state setter 函数 (
setIndex) 可以更新 state 变量并触发 React 重新渲染组件。
以下是实际发生的情况:
scss
const [index, setIndex] = useState(0);
- 组件进行第一次渲染。 因为你将
0作为index的初始值传递给useState,它将返回[0, setIndex]。 React 记住0是最新的 state 值。 - 你更新了 state 。当用户点击按钮时,它会调用
setIndex(index + 1)。index是0,所以它是setIndex(1)。这告诉 React 现在记住index是1并触发下一次渲染。 - 组件进行第二次渲染 。React 仍然看到
useState(0),但是因为 React 记住 了你将index设置为了1,它将返回[1, setIndex]。 - 以此类推!
赋予一个组件多个 state 变量
你可以在一个组件中拥有任意多种类型的 state 变量。该组件有两个 state 变量,一个数字 index 和一个布尔值 showMore,点击 "Show Details" 会改变 showMore 的值:
javascript
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
function handleNextClick() {
setIndex(index + 1);
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<button onClick={handleMoreClick}>
{showMore ? 'Hide' : 'Show'} details
</button>
{showMore && <p>{sculpture.description}</p>}
<img
src={sculpture.url}
alt={sculpture.alt}
/>
</>
);
}
State 是隔离且私有的
State 是屏幕上组件实例内部的状态。换句话说,如果你渲染同一个组件两次,每个副本都会有完全隔离的 state!改变其中一个不会影响另一个。