作者:胡子大哈
原文链接: http://huziketang.com/books/react/lesson1
看了 react.js 小书 第二阶段的内容,边看边做一点记录,整理了一些知识点。仅供学习。
17.前端应用状态管理 —— 状态提升
当某个状态被多个组件依赖或者影响的时候,就把该状态提升到这些组件的最近公共父组件中去管理,用 props 传递数据或者函数来管理这种依赖或者影响的行为。
如何更好的管理这种被多个组件所依赖或影响的状态?
你可以看到 React.js 并没有提供好的解决方案来管理这种组件之间的共享状态。在实际项目当中状态提升并不是一个好的解决方案,所以我们后续会引入Redux
这样的状态管理工具来帮助我们来管理这种共享状态,但是在讲解到 Redux 之前,我们暂时采取状态提升的方式来进行管理。
18.挂载阶段的组件生命周期(一)
我们把 React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载。
你可以理解一个组件的方法调用是这么一个过程:
-> constructor()
-> componentWillMount()
-> render()
// 然后构造 DOM 元素插入页面
-> componentDidMount()
// ...
// 即将从页面中删除
-> componentWillUnmount()
// 从页面中删除
componentWillMount
和componentDidMount
都是可以像 render 方法一样自定义在组件的内部。挂载的时候,React.js 会在组件的 render 之前调用 componentWillMount,在 DOM 元素塞入页面以后调用 componentDidMount。
总结:
componentWillMount:组件挂载开始之前,也就是在组件调用 render 方法之前调用。
componentDidMount:组件挂载完成以后,也就是 DOM 元素已经插入页面后调用。
componentWillUnmount:组件对应的 DOM 元素从页面中删除之前调用。
19.挂载阶段的组件生命周期(二)
我们一般会把组件的 state 的初始化工作放在 constructor 里面去做;在 componentWillMount 进行组件的启动工作,例如 Ajax 数据拉取、定时器的启动;组件从页面上销毁的时候,有时候需要一些数据的清理,例如定时器的清理,就会放在 componentWillUnmount 里面去做。
说一下本节没有提到的 componentDidMount 。一般来说,有些组件的启动工作是依赖 DOM 的,例如动画的启动,而 componentWillMount 的时候组件还没挂载完成,所以没法进行这些启动工作,这时候就可以把这些操作放在 componentDidMount 当中。componentDidMount 的具体使用我们会在接下来的章节当中结合 DOM 来讲。
20.更新阶段的组件生命周期
组件的挂载指的是将组件渲染并且构造 DOM 元素然后插入页面的过程。这是一个从无到有的过程。
除了挂载阶段,还有一种“更新阶段”。说白了就是 setState 导致 React.js 重新渲染组件并且把组件的变化应用到 DOM 元素上的过程,这是一个组件的变化过程。
关于更新阶段的组件生命周期:
shouldComponentUpdate(nextProps, nextState)
:你可以通过这个方法控制组件是否重新渲染。如果返回 false 组件就不会重新渲染。这个生命周期在 React.js 性能优化上非常有用。
componentWillReceiveProps(nextProps)
:组件从父组件接收到新的 props 之前调用。
componentWillUpdate()
:组件开始重新渲染之前调用。
componentDidUpdate()
:组件重新渲染并且把更改变更到真实的 DOM 以后调用。
21.ref 和 React.js 中的 DOM 操作
React.js 重新渲染的机制帮助我们免除了绝大部分的 DOM 更新操作,也让类似于 jQuery 这种以封装 DOM 操作为主的第三方的库从我们的开发工具链中删除。
但是 React.js 并不能完全满足所有 DOM 操作需求,有些时候我们还是需要和 DOM 打交道。比如说你想进入页面以后自动 focus 到某个输入框,你需要调用 input.focus() 的 DOM API,比如说你想动态获取某个 DOM 元素的尺寸来做后续的动画,等等。
React.js 当中提供了 ref 属性来帮助我们获取已经挂载的元素的 DOM 节点,你可以给某个 JSX 元素加上 ref属性。
class AutoFocusInput extends Component {
componentDidMount () {
this.input.focus()
}
render () {
return (
this.input = input} />
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
我们给 input 元素加了一个 ref 属性,这个属性值是一个函数。
当 input 元素在页面上挂载完成以后,React.js 就会调用这个函数,并且把这个挂载以后的 DOM 节点传给这个函数。
在函数中我们把这个 DOM 元素设置为组件实例的一个属性,这样以后我们就可以通过 this.input 获取到这个 DOM 元素。
然后我们就可以在 componentDidMount 中使用这个 DOM 元素,并且调用 this.input.focus() 的 DOM API。整体就达到了页面加载完成就自动 focus 到输入框的功能。
22.props.children 和容器类组件
使用自定义组件的时候,可以在其中嵌套 JSX 结构。嵌套的结构在组件内部都可以通过props.children
获取到,这种组件编写方式在编写容器类型的组件当中非常有用。而在实际的 React.js 项目当中,我们几乎每天都需要用这种方式来编写组件。
23.dangerouslySetHTML 和 style 属性
React.js 中的元素的 style 属性的用法和 DOM 里面的 style 不大一样,普通的 HTML 中的:
React.js 小书
在 React.js 中你需要把 CSS 属性变成一个对象再传给元素:
React.js 小书
style 接受一个对象,这个对象里面是这个元素的 CSS 属性键值对,原来 CSS 属性中带 - 的元素都必须要去掉 - 换成驼峰命名,如 font-size 换成 fontSize,text-align 换成 textAlign。
24.PropTypes 和组件参数验证
都说 JavaScript 是一门灵活的语言,灵活性体现在弱类型、高阶函数等语言特性上。而语言的弱类型一般来说确实让我们写代码很爽,但是也很容易出 bug。
大型应用程序的构建其实更适合用强类型的语言来构建,它有更多的规则,可以帮助我们在编写代码阶段、编译阶段规避掉很多问题,让我们的应用程序更加的安全。
JavaScript 因为它的弱类型,常常意味着不是很安全。所以近年来出现了类似 TypeScript 和 Flow 等技术,来弥补 JavaScript 这方面的缺陷。
React.js 就提供了一种机制,让你可以给组件的配置参数加上类型验证。
我们这里先安装一个 React 提供的第三方库 prop-types:
npm install --save prop-types
导入import PropTypes from 'prop-types'
static propTypes = {
comment: PropTypes.object
}
我们可以通过 isRequired 关键字来强制组件某个参数必须传入:
...
static propTypes = {
comment: PropTypes.object.isRequired
}
...
React.js 提供的 PropTypes 提供了一系列的数据类型可以用来配置组件的参数:
PropTypes.array
PropTypes.bool
PropTypes.func
PropTypes.number
PropTypes.object
PropTypes.string
PropTypes.node
PropTypes.element
...
25.实战分析:评论功能(四)
持久化用户名
用户输入用户名,然后我们把用户名保存到浏览器的 LocalStorage 当中,当页面加载的时候再从 LocalStorage 把之前保存的用户名显示到用户名输入框当中。
...
componentWillMount () {
this._loadUsername()
}
_loadUsername () {
const username = localStorage.getItem('username')
if (username) {
this.setState({ username })
}
}
_saveUsername (username) {
localStorage.setItem('username', username)
}
handleUsernameBlur (event) {
this._saveUsername(event.target.value)
}
...
监听用户名输入框失去焦点的事件,就存储用户名。
组件挂载的时候把用户名加载出来。这是一种数据加载操作,不依赖 DOM 操作的组件启动的操作都可以放在 componentWillMount 中进行,加载用户名。
组件的私有方法都用 _ 开头,所有事件监听的方法都用 handle 开头。把事件监听方法传给组件的时候,属性名用 on 开头。
另外,组件的内容编写顺序如下:
- static 开头的类属性,如 defaultProps、propTypes。
- 构造函数,constructor。
- getter/setter。
- 组件生命周期。
- _ 开头的私有方法。
- 事件监听方法,handle*。
- render开头的方法,有时候 render() 方法里面的内容会分开到不同函数里面进行,这些函数都以 render 开头。
- render() 方法。
26.实战分析:评论功能(五)
持久化评论
对象的存取要解析成JSON字符串
_loadComments () {
let comments = localStorage.getItem('comments')
if (comments) {
comments = JSON.parse(comments)
this.setState({ comments })
}
}
_saveComments (comments) {
localStorage.setItem('comments', JSON.stringify(comments))
}