React在工作中的使用总结
Opened this issue · 2 comments
1.React组件的生命周期:
React 生命周期分为三种状态 1. 初始化 2.更新 3.销毁
1. 初始化阶段
- getDefaultPorps
设置默认的props,也可以用来设置组件的的默认属性,只调用一次
- getInitialState
初始化组件state的值,其返回值会赋值给组件的this.state的属性,只调用一次
- componentWillMount
该方法在完成渲染之前调用,在这里可以对组件的state最后的一次修改机会。
- render
创建虚拟DOM,进行diff算法,更新dom树来在这里进行,此时就不能修改state
- componentDidMount
render方法成功调用,并且真实的DOM都已经被渲染。在这里可以对数据进行请求
2. 更行的阶段
- componentWillReceiveProps:
nextProps 代表从父组件获取到新的props——组件接受到新的props,会触发该函数,父组件的状态改变,给子组件传入新的prop值。用于组件的props变化后,更新state。
- shouldComponentUpdate
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候)
- componentWillUpdate
组件初始化不调用,组件需要更新的时候调用,此时可以修改state
- render
- componentDidUpdate
组件初始化不调用,组件更新完成后调用,此时可以获取dom节点
3. 销毁的阶段
- componentWillUnmount
组件将要卸载的时候调用,一定事件监听、定时器需要在这里清除。
2. webpack-dev-server与react-hot-loader两者的热更新替换有什么区别?
- webpack-dev-server自己的--hot模式只能即时的刷新的页面,但状态保持不住,因为会重新的去刷新页面
- react-hot-loader是在--hot的基础上做了额外的处理,来保证状态可以存下来,支持react组件状态不会丢失。根据stateNode节点的更新对比,只更新改变的reactDOM节点,从而保留了未改变的state的值。
react-hot-loader开启的流程:
- 启动webpack-dev-server命令:1、添加--inline。2、在webpack.config.js添加devServer: {inline: true}; 当配置中没有--inline选项的时候,我们需要在entry里面添加webpack-dev-server/client?http://localhost:port
- webpack-dev-server启动inline模式、且允许热更新hot,在plugins添加HotModuleReplacementPlugin()
- 在entry的入口entry的参数,添加react-hot-loader/patch
- 入口文件index.js 引入react-hot-loader的AppContainer组件,在这个组件下的所有的子组件都会发生变化,才会触发热更新。
3. React RouterV4 在react中的使用
React RouterV4遵循了React的理念:万物皆组件。
React RouterV4基于Learn管理多个Repository,其代码库包括:
- react-router React Router的核心
- react-router-dom 用于DOM绑定的React Router
- react-router-native 用于React Native的React Router
- react-router-redux React Router和Redux的集成
- react-router-config 静态路由的配置帮组助手
4. this.setState更新的问题
- this.setState是异步的,所以在this.setState之后立即调用this.state是获取不到最新的数据的。
下面介绍三种方法来获取最新的数据:
1.回调函数
this.setState({
val: this.state.val + 1
}, () => {console.log(this.state.val)});
2.利用componentDidUpdate,因为在这个生命周期函数,说明页面已经渲染完成state、props都已经被渲染好了
componentDidUpdate(){
console.log(this.state.val);
}
3.将this.setState放入到setTimeout函数中(因为在this.setState之后this.state是立即更新的!)
let self = this;
setTimeout(function(){
self.setState({
val: this.state.val + 1
});
console.log(self.state.val);
})
5. this.props.children容器类组件
- 容器内组件,顾名思义就是里面的什么内容都不确定,只要负责装东西,它只是定义了外层结构形式,我们可以往里面塞任意的内容。
- 在使用的自定义组件的时候,我们可以在里面嵌套jsx代码。嵌套的结构在组件的内部可以通过props.children获取。
- 如果你在父组件的内部添加一个函数,在当前的组件中就可以通过this.props.children获取到这个函数(自己传入参数,但是函数是从父类传过来的)相当于vue的slot
- this.props.chuldren是一个数组(它在当前的组件里面),是可以获取从父组件需要展示的列表项的内容!!!。
- this.props 代表从父组件中获取到的内容
6. Props和State
- React的核心**就是组件化的**,组件根据props和state两个参数,计算得到对应的界面的UI。可见,props和state是组件两个重要的数据来源。
- props是组件对外的接口,state是组件对内的接口。组件内部可以引用其他的组件,组件之间的引用就形成了一个树状的结构(组件树)。组件的props数据,上层组件就可以通过下层组件的props属性进行传递。组件自身的也需要维护组件的数据,这就是对内的state。
如何设置一个组件state?
state必须能代表一个组件的UI呈现的完整状态集,组件对应UI的任何变化,都可以从state变化中反映出来,同时,state还必须是代表一个组件UI呈现的最小状态集。
- 变量是通过props从父组件获取的,则不是一个状态。
- 变量在组件的整个生命周期都保持不变的,则不是一个状态。
- 变量是可以通过state或props的已有数据获取得到的,则不是一个状态。
- 变量不在组件render中使用,则不是一个状态。这个变量更适合定义为组件的一个普通的属性。
如何正确的修改state
- 不能直接修改state
直接修改state,组件不会重新触发render
- state的更新是异步的
setState只是把修改的状态放入一个队列中,React会优化真正的时机,并且React会出于性能的问题,可能会多次将setState的状态修改合并成一次状态的修改。props的更新也是异步的。例如当我们连续点击两次按钮,连续调用两次this.setState,在React合并多次修改为一次的情况下,相当执行了以下的代码。后面的操作会覆盖前面的操作,最终的购买数量就只增加1个。
// 错误 这里只会触发一个添加的事件
Object.assign(
previousState,
{quantity: this.state.quantity + 1},
{quantity: this.state.quantity + 1}
)
// 正确 接收一个函数作为参数的setState、第一个参数是组件修改组件前的state,后面是组件最新的props
this.setState((preState, props) => {
counter: preState.quantity + 1,
})
3.state更新是一个浅合并(Shallow Merge)的过程
当调用setState修改组件的时候,只需要传入发生改变的状态变量,而不是组件完整的state,因为组件的的更新是一个浅合并的过程。只需要修改我们需要去修改的即可。
React建议我们把state当作一个不可变的对象,
- 状态的类型是不可变类型(数字、字符串、布尔值、null、undefined)
- 状态的类型是数组
如有一个数组类型的状态books,当向books中增加一本书时,使用数组的concat方法或ES6的数组扩展语法(spread syntax)
// 方法一 concat
this.setState(preState => ({ // 返回一个对象 return {...}
books: preState.books.concat(['React Book']) // concat用于连接一个或多个数组。
}))
// 方法二 ES6 展开运算符
this.setState(preState => {
books: [...preState.books, 'React Book']
}))
还可以使用slice来截取数组,使用filter来过滤数组,在setState里面参数添加一个函数,函数的参数是修改前的state。(concat、filter、slice返回一个新的数组,主要不要使用push、pop、shift、unshift、splice等方法修改数组,因为它们是在原数组的基础上修改的。)
3. 状态的类型是简单的对象
修改state中对象的方法的时候,以下的两种方式:
this.state = {
owner = {
name: '李杰',
age: 20
}
}
// 使用ES6的Object.assign方法
this.setState(preState => ({
// Object.assign用于将所有可枚举属性的值从一个或多个源对象复制到目标对象
owner: Object.assign({}, preState.owner, {name: 'lijie'})
})
)
// 对象的展开运算符
this.setState(preState => ({
owner: {...preState.owner, name: 'lijie'}
})
)
=> React建议state不可变对象,所以当组件某个状态发生改变的时候,我们应该重新去创建一个新的状态,而不是修改原来的状态。避免重新去直接修改原对象的方法。
为什么React推荐组件的状态不可变对象?
- 一是方便管理和调试
- 二是出于性能的考虑,组件的状态是不可变的对象的时候,我们在组件的shouldComponentUpdate的方法中,仅需要比较状态的引用就可以判断状态是否改变,从而避免了不必要的render方法的调用。
学会去在react中使用箭头函数,箭头函数中的this指向为:当前调用对象的所在的上下文环境,也就是当前的组件。() => {}
问题:React.createClass会自动绑定每个方法的this到当前组件中,但是使用ES6 class或者纯函数的时候,我们需要手动去绑定this!
React中绑定this的几种方法:
- bind:直接在方法的内部添加bind
render () {
return (
<button onClick={ this.handleClick.bind(this, '李杰') }>点我、点我</div>
)
}
- 构造函数内绑定
constructor(props) {
super(props);
this.handleClick = this.handle.bind(this);
}
// 这里我们也可以使用ES7的语法糖
constructor(props) {
super(props);
this.handleClick = ::this.handleClick;
}
- 箭头函数:会捕捉其所在的上下文this的值,作为自己的this的值。
render () {
return (
<button onClick={ () => this.handleClick() }>点我、点我</div>
)
}
// 当然我们还可以这么写
handleClick = (e) => {
console.log(this.state.message)
}
render () {
return (
<button onClick={ this.handleClick }>点我、点我</div>
)
}