温习react文档(v0.17)
前言:自己当初学习的react的还是0.8.x版本,如今技术日新月异,react已经进化到v17.x版本。很多语法已经跟之前不同,所以重新温习一下
State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState()
调用合并成一个调用。
因为 this.props
和 this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
例如,此代码可能会无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要解决这个问题,可以让 setState()
接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
上面使用了箭头函数,不过使用普通的函数也同样可以
事件处理
在 React 中另一个不同点是你不能通过返回 false
的方式阻止默认行为。你必须显式的使用 preventDefault
。例如,传统的 HTML 中阻止链接默认打开一个新页面,你可以这样写:
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
在 React 中,可能是这样的:
function ActionLink() {
function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); }
return (
<a href="#" onClick={handleClick}> Click me
</a>
);
}
在这里,e
是一个合成事件。React 根据 W3C 规范来定义这些合成事件,所以你不需要担心跨浏览器的兼容性问题。React 事件与原生事件不完全相同。如果想了解更多,请查看 SyntheticEvent
参考指南。
事件中的this
// 为了在回调中使用 `this`,这个绑定是必不可少的 this.handleClick = this.handleClick.bind(this);
或
<button onClick={() => this.handleClick()}> Click me </button>
向事件处理程序传递参数
在循环中,通常我们会为事件处理函数传递额外的参数。例如,若 id
是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:
<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
会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind
的方式,事件对象以及更多的参数将会被隐式的进行传递。
key
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs <li key={index}> {todo.text}
</li>
);
我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。可以看看 Robin Pokorny 的深度解析使用索引作为 key 的负面影响这一篇文章。
https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318
受控组件
在 HTML 中,表单元素(如<input>
、 <textarea>
和 <select>
)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。
我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。
例如:
this.setState({
[name]: value});
等同 ES5:
var partialState = {};
partialState[name] = value;this.setState(partialState);
这里使用了 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:
组合 vs 继承
包含关系
有些组件无法提前知晓它们子组件的具体内容。在 Sidebar
(侧边栏)和 Dialog
(对话框)等展现通用容器(box)的组件中特别容易遇到这种情况。
我们建议这些组件使用一个特殊的 children
prop 来将他们的子组件传递到渲染结果中:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children} </div>
);
}
这使得别的组件可以通过 JSX 嵌套,将任意组件作为子组件传递给它们。
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title"> Welcome </h1> <p className="Dialog-message"> Thank you for visiting our spacecraft! </p> </FancyBorder>
);
}
<FancyBorder>
JSX 标签中的所有内容都会作为一个 children
prop 传递给 FancyBorder
组件。因为 FancyBorder
将 {props.children}
渲染在一个 <div>
中,被传递的这些子组件最终都会出现在输出结果中。
少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 children
,而是自行约定:将所需内容传入 props,并使用相应的 prop。
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left} </div>
<div className="SplitPane-right">
{props.right} </div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts /> }
right={
<Chat /> } />
);
}
<Contacts />
和 <Chat />
之类的 React 元素本质就是对象(object),所以你可以把它们当作 props,像其他数据一样传递。这种方法可能使你想起别的库中“槽”(slot)的概念,但在 React 中没有“槽”这一概念的限制,你可以将任何东西作为 props 进行传递。
特例关系
有些时候,我们会把一些组件看作是其他组件的特殊实例,比如 WelcomeDialog
可以说是 Dialog
的特殊实例。
在 React 中,我们也可以通过组合来实现这一点。“特殊”组件可以通过 props 定制并渲染“一般”组件:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title} </h1>
<p className="Dialog-message">
{props.message} </p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog title="Welcome" message="Thank you for visiting our spacecraft!" /> );
}
组合也同样适用于以 class 形式定义的组件。
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children} </FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login} onChange={this.handleChange} /> <button onClick={this.handleSignUp}> Sign Me Up! </button> </Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
那么继承呢?
在 Facebook,我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。
Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。
批注:这个知识点对我来说,也是新鲜的。