本文是我在重新学习react文档时针对自己之前忽视或易被忽视细节的整理,对应的React版本v15.6.1。原文react官方文档
这篇文章初衷是帮助自己加深记忆和理解,如有我理解不对的地方,还请大家指正。
Hello World
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
JSX
- JSX被赋值给一个变量或常量是可以在两端加入小括号来调整格式
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
- JSX中可以运行js,需要在两端加入大括号
- JSX表达式就像一种JS对象一样,可以被赋值,传参,return
- JSX中可以设置属性,也可以用大括号将JS表达式写入属性中
- 空标签,需要最后用
/>
结尾 -
class
需要写为className
,tabindex
需要写为tabIndex
- JSX最后通过
React.createElement
将JSX转化为React Object
渲染Element
- element不同于组件
- element是一成不变的,一旦元素被创建,就不能修改它的children和属性了,代表了某一时刻UI的快照
- 截止目前,更新UI的方法只有新建一个element然后再次传入
React.render
- React app大多只调用一次
ReactDOM.render
- React只在必要时更新DOM,即使每次均传入一个新的元素到
React.render
,也只有其中被更新的子元素会被修改。
组件和属性(props)
- 组件定义可以通过函数或者类
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
基于class
会有额外的特性,后续展开
- 自定义组件应大写首字母
- 组件使用需要组件定义在作用域内
- 组件之间可以互相封装,组件最外层一定要返回一个单独的根元素
- 属性是只可读的
State和生命周期
- 相对于props,state是私有且完全被组件控制的
- 如果定义组件的state,就需要采用类的方法整理组件(class语法),这里需要注意基于class开发后,props需要通过
this.props
读取,state同样通过this.state
读取 - 需要给组件加入构造函数来初始化状态
constructor(props) {
super(props);
this.state = {date: new Date()};
}
注意由于继承时子类没有自己的this
,一定要调用super(props)
- 组件的生命周期中,初始化被称为
mounting
,卸载被称为unmounting
,所以可以在组件的这两个阶段定义方法componentDidMount
和cpmponentWillUnmount
- 组件的一些相关属性可以直接保存在
this
上,当然这些属性应该是不和渲染直接相关的,否则就要保存在this.state
中 - 组件的一些方法也可以像
render
一样定于与class上,之后通过this.XXX()
调用 - 除去render之外的方法中可以通过
this.setState({
date: new Date()
});
从而来修改state
- 不要直接修改state,这不会重新触发渲染,一定采取
setStete
的方法,只有在constructor中才可以通过this.state
来初始化state - state和props的改动可能是异步更新的,所以如果需要基于前一状态来更新state,应该通过如下方式
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
-
setState
函数内部是在marge的,被改动的会被更新,未改动的会被保存 - 组件的state应该是封装在内部的,只有当他选择传递时才可以暴露在外,比如父组件可以通过设置子组件属性的方式与子组件通信,这是一种自顶向下的数据流
<FormattedDate date={this.state.date} />
事件绑定
- 可以通过如下方式进行事件绑定
<button onClick={activateLasers}>
Activate Lasers
</button>
但是不能通过return false;
来阻止默认行为发生,一定要调用e.preventDefault();
实现
- 事件绑定触发的方法可以定义在class内,之后通过
this
访问 - 如果事件绑定的函数中有对this的访问,那么需要在constructor中进行this的绑定
this.handleClick = this.handleClick.bind(this);
原因在于我们是通过onClick={this.handleClick}
读取的函数方法,而不是this.handleClick()
方法二是借助property initializer syntax,如下(create-react-app中可用)
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
方法三基于箭头函数,因为箭头函数的作用域在定义时被决定
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
方法三中每次渲染时会绑定不同的函数,因而可能触发组件的再次渲染,因而推荐方法一、二
条件渲染
- 可以根据state和props进行判断,return出不同的渲染结果
- 可以将不同JSX保存在变量中,之后在return中调用这个变量
- 可以通过短路运算符&&来写出更加简明的条件渲染
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
- 也可以通过三目运算符完成条件渲染
- 如果希望一个组件不显示,可以在render中return null。这只使组件不显示,不会影响其生命周期
Lists and Keys
- 在JSX中用{}包裹一个数组元素(元素用JSX表示),那么就可以渲染出一个元素list。其中每个元素可以再添加属性,如key。如果希望渲染一个list的元素,一定要加上key属性来帮助react区分元素。list中的key一定要不同
key最好选用不同的字符串,不建议采用index进行区分
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
- key应该在被从array通过map得到list时添加,而不是在list中每一项的子元素上再添加
- key在通过数组渲染的siblings之间应该是互不相同的,但是不用组的siblings之间是可以存在重复的
- key只是为了帮助React的暗示信息,并不会真的被传送到组件上,如组件需要,可以再加入其它props
Forms
Controlled Components
- 指值被React所控制的输入表单元素
- 表单元素的value一旦在JSX中被设置,如
<input type="text" value={this.state.value} onChange={this.handleChange} />
那么它所显示的值就是this.state.value
,通过onChange
中绑定的函数修改this.state.value
,从而控制这个表单元素输入的值,让其随用户输入而更新
- 用户的输入时对表单元素的修改和有效性检测等,此时就被直接控制了
- select组件,通过设置
value
与某个option中的value相同,来表示这个option被选中 - 对于一个组件内的多个表单元素,可以通过设置
name
,在一个函数通过e.target.name
来区分更改state,这样就可以用一个函数来监听多个元素的输入 - uncontrolled component在某些情境下,可能是更灵活的针对表单的方案
Lifting State Up
对于一个在多个组件间需要同步的数据,可以将其提升到最近的共同祖先组件的stete上,之后通过props传递给各个子组件,使各个子组件中取到同一个值。也是是自顶向下数据流思想的一种体现
Composition vs Inheritance(组成与继承)
- 类似boxes模式的组件,可以通过
children
属性访问在其内部写入的其他通过JSX表达的组件
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
- 一些组件可以理解为一些通用组件的特例化,这种特殊组件可以返回被设置了特有props值得通用组件实现
- 综上,用组合的方法开发各个组件是推荐的,继承并不十分实用
Thinking in React
- 基于React开发,可以先从静态页面入手,此时不应引入state,因为state应面向交互
- 衡量是否采用state三个角度:是否来自父组件的传递,是否总是不变的,是否可以通过其他state或者props得到
- React数据流除自顶向下,同样可以自下向上,这需要将在顶层组件中设置修改顶层state的方法,之后将这个方法作为属性传入子组件,子组件调用这个方法,即可修改顶层组件中的state