一、Hello World
1. 最简易的 React 示例
ReactDOM.render(
'<h1>Hello World</h1>',
document.querySelector('#root')
)
- React Apps 的构建块就是:元素和组件(elements 和 components)
- 只要掌握了它们就可以重复使用这些块构建复杂的应用程序
二、JSX 语法
const element = <h1>Hello, world!</h1>;
JSX 是 JavaScript 的语法扩展,它看起来有点像模板语言,而其实拥有 JavaScript 的全部功能
React 认为处理逻辑与 UI 本质上是耦合的
- 如何处理事件
- 状态如何随时间变化
- 数据如何为展现做准备
与通过将标记和逻辑放在单独的文件中来认为将技术分离不同,React 通过称为“组件”的松散耦合的单元来分离关注点,它既包含标记,也包含逻辑。
React 不需要 JSX,但是在使用时将 UI 放入 JavaScript 代码能对视觉帮助很有用。它还允许帮助 React 显示更多有用的错误和警告消息。
1. JSX 中嵌入表达式
const name = 'Josh Perez'
function formatName(user) {
return user.firstName + ' ' + user.lastName
}
const element = (<h1>Hello, {name}</h1>)
const element2 = (
<h1>
Hello, {formatName(user)}!
</h1>
)
- 可以把任何有效的 JavaScript 表达式放入 JSX 的大括号中
- JSX 拆分成多行时,建议加上圆括号
经过编译后,JSX 表达式变成了普通的函数调用,然后计算得到 JavaScript 对象
- 我们可以很轻松使用 if 语句、for 循环使用 JSX,它可以被赋值给变量,可以作为函数的参数,也可以作为函数的返回值
3. JSX 中指定属性
const element = (<div tabIndex="0"></div>)
const element = (<img src={user.avatarUrl}></img>)
- 可以使用引号将字符串指定为属性
- 可以使用大括号将 JavaScript 表达式嵌入到属性中
- JSX 的语法更接近 JS 而不是 HTML,React DOM 使用驼峰属性的命名原则,而不是 HTML 属性名
4. JSX 中指定子元素
const element = <img src={user.avatarUrl} />
const element2 = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
)
- 如果一个标签的内容是空的,可以像 XML 一样使用
/>
立刻关闭
5. JSX 防止注入攻击
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;
- 默认情况下,React DOM 会渲染 JSX 内容之前转译所有嵌入其中的值,所以在应用里不会注入任何东西
- 所有东西在渲染之前都会转换成字符串,这能防止 XSS(cross site scripting)攻击
6. JSX 表示为对象
const element = (
<h1 className="greeting">
Hello, world!
</h1>
)
const element2 = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
)
- Babel 将 JSX 编译为 React.createElement() 调用,上面两个写法是等价的
- React.createElement() 执行一些检查来帮助我们写一些没有 bug 的代码,它实际创建了这样一个对象
- 这些对象被称为“React 元素”,React 读取它们来构造 DOM, 并让 DOM 与最新 data 保持同步更新
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
}
三、渲染元素
const element = (<h1>Hello, world</h1>)
- 元素是 React Apps 的最小构建块,一个元素描述屏幕中我们想看到的东西
- 与浏览器 DOM 元素不同,React 元素是普通对象,创建的开销很小
- React DOM 负责更新 DOM,将它与 React 元素保持一致
- 不要将“元素”与“组件”混淆了,它们的关系是:组件是由元素组成的
1. 将元素渲染成 DOM
<div id="root"></div>
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
- 在HTML 选择一个 root DOM,它内部的所有内容都被 React DOM 管理
- 要将 React 元素渲染成 root DOM,就把 React 元素和 root DOM 传递给 ReactDOM.render()
2. 更新渲染的元素
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
- React 元素是不可变的,一旦创建了一个元素,就不能改变它的子元素和属性
- 我们可以创建一个新的元素,然后将其传递给 ReactDOM.render(),上面是一个滴答时钟的示例
3. React 只更新必要的部分
- React DOM 将元素与其子元素与先前的做比较,然后只更新 DOM 能够达到状态的所必要的部分
- 上个示例中,即使我们在每次“滴答”创建了一个新的元素来描述 UI 树,但只有文本节点的内容改变了, React DOM 才去更新它
四、组件 & Props
- 组件可以使我们将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思
- 从概念上看,组件类似于 JavaScript 函数
- 它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素
1. 函数组件和类组件
// 函数组件
function Welcome(props) {
return (<h1>Hello, {props.name}</h1>)
}
// 类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
- 定义一个组件最简单的方法是编写一个 JavaScript 函数
- 示例中,它接受一个带有数据的单个“ props”(代表属性)对象参数,并返回一个 React 元素
- 我们称这些组件为“函数组件”,因为它们实际上是 JavaScript 函数
- 我们也可以使用
ES6 class
定义一个组件,从 React 角度看,他们是等价的
2. 渲染一个组件
function Welcome(props) {
return (<h1>Hello, {props.name}</h1>)
}
const element = (<Welcome name="Sara" />)
ReactDOM.render(
element,
document.getElementById('root')
)
- React 元素不仅可以代表 DOM 标签,也可以表示为用户定义的组件
- 当 React 看到一个元素表示为用户自定义组件时,它会把 JSX 的属性和子元素作为一个对象传递给它
- 这个对象被称为“props”
- 始终将组件以大写字母开头,React 会以小鞋字母开头的组件视为 DOM 标记
- 比如 表示一个 HTML div 标记
- 而
表示一个组件,并且 Welcome 需要在作用域内使用
3. 组件之间组合
function Welcome(props) {
return (<h1>Hello, {props.name}</h1>)
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
- 组件可以在其输出中引用其他组件,这就可以让我们用同一组件做任意级别的抽象
- 一个按钮,一个对话框,或是一个整个的屏幕,所有这些通常都以组件组件的形式表达
- 通常,一个 React Apps 在顶层有一个
App
组件 - 而如果是将 React 集成到现有的应用中,可能会从小组件,比如
Button
组件开始自底向上工作到视图中
4. 提取组件
// 初始的 Comment 组件
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 组件
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
// 提取 UserInfo 组件
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
// 拆分后的 Comment 组件
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>
);
}
- 不要害怕将组件拆分成更小的组件
- 建议从组件自身的角度命名组件,而不是根据它被使用的上下文
- 提取组件一开始看起来是个繁重的工作,但在大型应用中,构建可复用组件库是值得的
5. Props 是只读的
// 这是一个纯函数,不修改输入
function sum(a, b) {
return a + b;
}
// 这个函数不纯,它改变了自己的输入
function withdraw(account, amount) {
account.total -= amount;
}
- 当声明了一个函数组件或类组件时,它一定不能修改它的 props
- 纯函数不会尝试改变它的输入,并会为相同的输入返回相同的结果
- 所有 React 组件必须像纯函数一样使用它们的 props
- 当然,UIs 是动态的,并会随着时间推移而变化,State 允许 React 组件随时间更改其输出,以相应用户操作、网络相应和其他任何东西,而不违反此规则
五、State & 生命周期
在「更新渲染的元素」小结中我们调用 ReactDom.render() 来改变渲染出的结果,当我们有了 State,我们就可以将滴答时钟封装成可复用的组件,它将设置它自己的定时器,并在每秒更新一次
1. 将函数组件转换为类组件
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- 把函数组件转换成类组件通常有 5 个步骤:
- 创建
ES6 class
类并继承React.Component
- 添加一个名叫
render()
的空方法 - 将函数体移到
render()
方法里 - 在
render()
里的props
替换成this.props
- 删除剩余的空函数声明
- 创建
render()
方法会在每次更新的时候被调用,一旦我们渲染<Clock />
到相同的 DOM 节点时,只有一个 Clock 类的实例会被使用,这让我们可以使用额外的特性,比如局部 state 和生命周期方法
2. 给 class 添加局部 State
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
- 添加一个初始赋值
this.state
的类构造函数 - 类组件应该始终使用
props
来调用构造函数
3. 给 class 添加生命周期方法
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
- 当
<Clock>
第一次被渲染成 DOM 时,我们设置一个定时器。React 中将这个阶段被称为“挂载” - 在
<Clock>
被移除时,我们将定时器清除。React 中将这个阶段被称为“卸载” - 我们可以在类组件上声明特殊的方法,以便在组件在挂载和卸载时运行一些代码
- 这些特殊的方法被称为“生命周期”
- componentDidMount() 方法在组件输出被渲染完毕后执行,是设置定时器的好时机
- 除了 this.props 和 this.state,我们可以自由地向勒中添加其他字段,比如 this.timeId
- 我们在 componentWillUnmount() 生命周期中清除定时器
- 最后我们实现一个
tick()
方法,让Clock
每秒运行一次,它使用this.setState()
来安排对局部 state 的更新
4. 正确地使用 State
// 1. 不要直接修改状态。相反,我们要使用 setState()
// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});
// 2. state 更新可能是异步的
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// 3. state 更新会合并
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
关于 setState(),我们应该要知道三件事
- 不要直接修改状态。相反,我们要使用
setState()
- 直接修改状态不会重新渲染组件
- state 更新可能是异步的
- React 为了提高性能会将一批
setState()
调用合并成一个 - 因此
this.state
和this.props
可能会异步更新,我们不能依赖的它们的值来计算下一个状态 - 如果想要正确依赖
this.state
和this.props
,可以使用 setState() 的第二个调用方式,它接受一个函数,第一个参数是 state,第二个参数是 props
- React 为了提高性能会将一批
- state 更新会合并
- 在调用
setState()
的时,React 将提供的对象合并到当前的 state 中,而这个合并是浅合并
- 在调用
5. 数据流向下流动
- 父组件和子组件不知道一个组件是有状态的还是无状态的,也不关心它是函数组件还是类组件
- 这就是为什么 state 被称为局部和封装的原因。除了它自己,任何其他组件无法访问它
- 一个组件可以选择是否将 state 作为 props 传递给它的子组件
- 这通常称为“自顶向下”或“单向数据流”,任何 state 总是由某个特定组件拥有,并且任何数据和 UI 只能影响 tree 中“低于”它们的组件
- 在 React Apps 中,组件是否有状态被认为是组件的实现细节,这些细节可能会随着时间推移而改变
- 我们可以在有状态组件内部使用无状态组件
- 也可以在无状态组件内部使用有状态组件
六、事件处理
1. 与 DOM 事件处理做对比
<button onclick="activateLasers()">
Activate Lasers
</button>
<form onsubmit="console.log('You clicked submit.'); return false">
<button type="submit">Submit</button>
</form>
<button onClick={activateLasers}>
Activate Lasers
</button>
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
React 元素中的事件处理和在 DOM 处理事件很类似,但有些许不同
- React 的事件名使用小驼峰式,而不是小写命名
- 在 JSX 中将函数作为事件处理,而不是字符串
- 在 React 不能返回 false 来组织默认事件,必须显性地调用
preventDefault
- 这里面的 e 是一个合成事件,React 根据 W3C 规范定义了这些合成事件
2. React 绑定事件的方法
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
// 使用公共类字段语法
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
// c. 事件函数使用箭头函数
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
- 在使用 React 时,通常不需要调用
addEventListener
在 DOM 元素创建后将监听器添加到该元素,相反,只需要在最初渲染元素时提供一个监听器 - 当使用
ES6类
定义组件时,常见的模式是将事件处理程序作为类上的方法 - 使用时我们要时刻注意 this 问题,handleClick 中的 this 需要保证指向 Toggle 实例,有三种方法保存
- 在构造函数里使用绑定 this
- 使用公共类字段语法
- 回调函数使用箭头函数包裹
- 第三种方法的问题在于每次渲染 LoggingButton 时会创建一个不同的回调,在大多数情况下,它很好,但是如果这个回调作为一个 props 传递给下面组件,那么这些组件可能会执行额外的渲染
- 所以通常建议在构造函数中绑定或使用类字段语法,以避免此类性能问题
3. 将参数传递给事件处理器
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
- 上面两行是等价的,分别使用箭头函数和
Function.prototype.bind
- 在这两种情况下,表示 React 事件的 e 将作为 ID 之后的第二个参数传递
七、条件渲染
1. 和 JavaScript 一样条件渲染
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
- React 中的条件渲染与 JavaScript 中的条件渲染工作方式相同,使用
if
或其他条件运算符就行了
2. 元素变量
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
- 使用变量存储元素,可以让我们有条件地渲染组件的一部分
3. 操作符 &&(内联的 if)
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
// 返回的 <div>0<div>
render() {
const count = 0;
return (
<div>
{ count && <h1>Messages: {count}</h1>}
</div>
);
}
- 将表达式包装在大括号中来在 JSX 中嵌入表达式,里面使用操作符 &&
- true && expression 总是计算为 expression,而 false & expression 总是计算为 false
- 如果条件为 true,&& 右边的元素就会是输出结果,如果条件是 false,React 就会直接跳过
- 注意,如果返回的 falsy 表达式,React 会渲染出 falsy 表达式!
4. ?:表达式(内联的 if-else)
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
- 使用 JavaScript 条件操作符
condition ? true: false
- 使用哪种条件渲染的方式取决于我们认为哪个更有可读性
- 当条件变得过于复杂时,它可能是提取成组件的好时机
5. 阻止组件渲染
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
- 在极少数的情况下,如果希望每个组件隐藏自己,而它由另一个组件渲染,可以 return null 达到目的
八、列表 && keys
1. 渲染多个组件
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
(<li>{number}</li>)
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
- 构建一个元素集合,并使用大括号 {} 将它们放入 JSX 中
2. 基本列表组件
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
- 通常我们会在一个组件中渲染列表
- 我们要为每个列表项分配一个 key
3. 使用 keys
const todoItems = todos.map((todo) =>
(<li key={todo.id}>
{todo.text}
</li>)
);
const todoItems2 = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
- keys 帮助 React 识别哪些项目被更改了、添加了、或被删除了
- 需要给列表中的每个元素 key,让元素具有稳定的标识
- 选择 key 的最好的方法就是使用一个在同级列表项中唯一标识它的字符串,大多数情况下是使用数据中 id
- 如果没有稳定的 id 给列表项,可以使用列表项索引作为最后的手段
- 如果项目的顺序可能发生变化,那么不建议用对键使用索引,这会对性能产生不好的影响
4. 用 keys 提取组件
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
- keys 只有在周围数组的上下文才有意义
- 一个很好的经验法则是 map() 中的元素需要使用 keys
5. key 在兄弟节点之间必须唯一
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
- 数组中的 key 在其兄弟数组中是唯一的,而不必是全局唯一,不同的数组可以使用相同的键
- key 用于 React 的提示,但不会传递给我们写的组件,如果想用它的值,需要显性地换个名字作为 props
6. JSX 中嵌入 map()
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
- JSX 允许在花括号中嵌入任何表达式,所以我们可以内联
map()
的结果 - 有时候这会导致更清晰的代码,但是这种风格会被滥用,如果 map() 嵌套得太多,可能是提取组件的好时机
1. 使用列表元素可以获得多倍快乐
React 元素的列表能够让我们获得多倍快乐,就像下面这样
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
)
2. 列表元素必须要有独一无二的 key
当运行以上代码会出现一个告警:Warning: Each child in an array or iterator should have a unique “key” prop. 意思是当创建一个元素时,必须包含一个独一无二的 key。
九、Forms
1. 原生的 form
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
- 在React 中,form 元素与其他 DOM 元素不同,因为 form 元素通常会保存一些内部状态,例如,原生 HTML 的 form 只接受一个名字
- 当用户提交表单,此表单由默认的行为
2. 受控组件,文字输入 input 标签
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
- 在 HTML 中,像