# 事件处理
事件处理 – React (docschina.org)
- 需要传入一个函数作为事件处理函数,而不是一个字符串。
<button onClick={activateLasers}>
Activate Lasers
</button>
- 不能通过返回
false
的方式阻止默认行为。必须显式的使用preventDefault
。
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
使用 React 时,你一般不需要使用
addEventListener
为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可。将事件处理函数声明为 class 中的方法。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
# 列表与 key
列表 & Key – React (docschina.org)
# key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key
# key 只是在兄弟节点之间必须唯一
数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值
# 表单
表单 – React (docschina.org)
在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。
# 受控组件
在 HTML 中,表单元素(如 <input>
、 <textarea>
和 <select>
)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。
我们可以把两者结合起来,使 React 的 state 成为 “唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做 “受控组件”。
# textarea 标签
在 HTML 中, <textarea>
元素通过其子元素定义其文本:
<textarea> | |
你好, 这是在 text area 里的文本 | |
</textarea> |
而在 React 中, <textarea>
使用 value
属性代替。这样,可以使得使用 <textarea>
的表单和使用单行 input 的表单非常类似:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: '请撰写一篇关于你喜欢的 DOM 元素的文章.' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
alert('提交的文章: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea value={this.state.value} onChange={this.handleChange} /> </label>
<input type="submit" value="提交" />
</form>
);
}
}
请注意, this.state.value
初始化于构造函数中,因此文本区域默认有初值。
#
# 状态提升
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。让我们看看它是如何运作的。
状态提升 – React (docschina.org)
我们将从一个名为 BoilingVerdict
的组件开始,它接受 celsius
温度作为一个 prop,并据此打印出该温度是否足以将水煮沸的结果。
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>; }
return <p>The water would not boil.</p>;}
接下来,我们创建一个名为 Calculator
的组件。它渲染一个用于输入温度的 <input>
,并将其值保存在 this.state.temperature
中。
另外,它根据当前输入值渲染 BoilingVerdict
组件。
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''}; }
handleChange(e) {
this.setState({temperature: e.target.value}); }
render() {
const temperature = this.state.temperature; return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input value={temperature} onChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset>
);
}
}
# 添加第二个输入框
我们的新需求是,在已有摄氏温度输入框的基础上,我们提供华氏度的输入框,并保持两个输入框的数据同步。
我们先从 Calculator
组件中抽离出 TemperatureInput
组件,然后为其添加一个新的 scale
prop,它可以是 "c"
或是 "f"
:
const scaleNames = { c: 'Celsius', f: 'Fahrenheit'};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale; return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend> <input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
我们现在可以修改 Calculator
组件让它渲染两个独立的温度输入框组件:
我们先从 Calculator
组件中抽离出 TemperatureInput
组件,然后为其添加一个新的 scale
prop,它可以是 "c"
或是 "f"
:
在 React 应用中,任何可变数据应当只有一个相对应的唯一 “数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。
虽然提升 state 方式比双向绑定方式需要编写更多的 “样板” 代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于 “存在” 于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。