每日, 每一段时间都在问自己自己干了什么, 看一下自己知道什么, 希望在不断进步的时候, 不断更新
这是将近大半年的第一次 react 自测
PS: 有些概念自问自答只是精炼我自己的看法, 有不对请大家不吝指出
react 基础
生命周期
写一下 react 的组件的生命周期吧
加载期
- constructor()
- componentWillMount()
- render()
- componentDidMount()
更新期
- componentWillReceiveProps()
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componeneDidUpdate()
销毁期
componentWillUnmout()
错误捕捉
componentDidCatch()
不要调用 setState 的生命周期
- shouldComponentUpdate()
- componentWillUpdate() // 这个周期是不能调用 会造成死循环
- componentWillUnmount()
将生命周期函数声明为 async 会对组件有影响吗?
没有, 函数内部可以使用 async/await 的语法糖
为什么可以将生命周期函数声明为 async function
因为即使是声明为 async 在调用的时候也是当成普通函数调用
综合应用一下
- main.js
- import React from 'react';
- import {render} from 'react-dom';
- import Parent from './parent';
- const styles = {
- fontFamily: 'sans-serif',
- textAlign: 'center',
- };
- class App extends React.Component {
- state = {
- mount:true
- }
- render(){
- return (
- <div style={styles}>
- {this.state.mount?<Parent />:null}
- <h2>Start editing to see some magic happen {'\u2728'}</h2>
- <button
- onClick={()=>{
- this.setState({
- mount:!this.state.mount
- })
- }}>test</button>
- </div>
- )
- }
- }
- render(<App />, document.getElementById('root'));
- parent.js
- import React from 'react';
- import ChildComponent from './child.js'
- export default class Parent extends React.Component {
- constructor(props) {
- super(props)
- console.log('parent:constructor')
- }
- componentWillMount() {
- console.log('parent: will mount')
- }
- componentDidMount() {
- console.log('parent: did mount')
- }
- componentDidUpdate() {
- console.log('parent: did update')
- }
- componentWillUnmount() {
- console.log('parent: will mount')
- }
- render() {
- console.log('parent render')
- return (
- <div>
- <ChildComponent refresh={()=>{
- console.log('did update A')
- this.setState({},()=>{
- console.log('did update B')
- })
- }}/>
- </div>
- )
- }
- }
- child.js
- import React from 'react';
- export default class extends React.Component{
- constructor(props){
- super(props)
- console.log('child:constructor')
- }
- componentWillMount(){
- console.log('child: will mount')
- }
- componentDidMount(){
- console.log('child: did mount')
- }
- componentDidUpdate(){
- console.log('child: did update')
- }
- componentWillUnmount(){
- console.log('child: will mount')
- }
- render(){
- console.log('child render')
- return (
- <div>
- <span>child Component </span>
- <button onClick={() => {
- this.props.refresh()
- }}>refresh</button>
- </div>
- )
- }
- }
题一请问在 App 第一次 render 的时候, 控制台输出的语句是什么?
- parent:constructor
- parent: will mount
- parent render
- child:constructor
- child: will mount
- child render
- child: did mount
- parent: did mount
题二请问在点击子组件 refresh 按钮按下的时候控制台输出的语句是什么?
- did update A
- parent render
- child render
- child: did update
- parent: did update
- did update B
题三请问在第一次点击 test 按钮的时候控制台会输出什么?
- parent: will mount
- child: will mount
引出, 为什么是这样的顺序?
解析 virtual Dom 的时候是递归的形式
组件更新是自顶向下
- TODO // 实现一下一个简单的 virtual Dom 解析
- setState
setState 是异步还是同步, 下面的输出结果是什么, 为什么?
异步
0,0,2,3
生命周期内合并执行, 合并执行就是将其并在一个事务中进行, 进行批量更新
- import React from "react";
- export
- default class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- count: 0
- };
- }
- componentDidMount() {
- this.setState({
- count: this.state.count + 1
- });
- console.log(this.state.count);
- this.setState({
- count: this.state.count + 1
- });
- console.log(this.state.count);
- setTimeout(() =>{
- this.setState({
- count: this.state.count + 1
- });
- console.log(this.state.count);
- this.setState({
- count: this.state.count + 1
- });
- console.log(this.state.count);
- });
- }
- render() {
- return <div> {
- this.state.count
- } </div>;
- }
- }/
setState 的第二个参数有什么用?
延伸, 为什么 setState 需要是异步的, 举个例子?
- class Parent extends React.Component{
- state = {
- a:1
- }
- render(){
- <Child a={this.state.a} increase={()=>{this.setState({
- a:this.state.a +1
- })}}/>
- }
- }
在同步的情况下 setState 以后父组件的 a 是 2, 但是在更新组件前 props 到子组件的 a 却还是 1 这样是不合理的
具体见 why setState is async
组件
PureComponent 和 Stateless Componet 的区别:
前者可以具有自身的 state, 有组件的生命周期, 后者不具备 PureCompoent 在 shouldComponentUpdate 上面进行简单的浅比较, 不适合用于处理复杂状态 Stateless Component 本身没有更新机制, 只能通过父组件更新
状态管理
redux
redux 的三大组成
action,store,reducer
redux 的三大原则, 讲讲了解
单一数据源, 数据只能通过 action 修改, reducer 必须是纯函数
单一数据源指的是, 全部的 store 应该挂载在一颗 store 上
总结起来就是单向数据流
关于 redux 中的异步数据流
redux 本身是不支持异步数据流的, 即在没有中间件的时候 dispatch 的必须是一个 Object(目前的理解是原型链的继承自 Object)
你了解处理异步 action 的中间件
redux-thunk
短小精悍的中间件
- const INCREMENT_MONEY = 'INCREMENT_MONEY'const mapDispatchToProps = (dispatch) =>({
- getMoney: () =>{
- setTimeout(() =>{
- dispatch({
- type: INCREMENT,
- })
- },
- 500)
- }
- })
- // ...
- redux-saga
适合处理比较复杂的异步问题, 一些简单场景也提供便利的功能, saga 中运行的任务是可取消的, 像触发同一个任务的时候可以通过 saga 的机制总是执行最新的任务
...
为什么 reducers 需要纯函数?
因为在遍历 store 中的 key 的时候, 判断前后状态是通过浅比较
mobx
通过 observable 包裹变量, 通过 Object.defineProperty 重写 get/set, 通过 atom 发送信号到 mobx 中去
更新机制, 通过 observer 进行绑定组件在绑定中通过 reaction 在 render 中收集依赖, 再 mobx 收到依赖发送的信号的时候向绑定的组件发送更新信号, observer 作为高阶组件再调用容器组件的 forceUpdate 达到更新组件的目的
实质, 数据双向绑定
拓展阅读
- import React from "react";
- export
- default class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- count: 0
- };
- }
- componentDidMount() {
- this.refBtn.addEventListener("click", this.onClick);
- }
- componentWillUnmount() {
- this.refBtn.removeEventListener("click", this.onClick);
- }
- onClick = () =>{
- console.log("before setState", this.state.count);
- this.setState(state =>({
- count: state.count + 1
- }));
- console.log("after setState", this.state.count);
- };
- render() {
- return ( <div> <button onClick = {
- this.onClick
- }> React Event </button>
- <button ref={ref => (this.refBtn = ref)}>Direct DOM event</button> </div>
- );
- }
- }/
- Why do we need middleware for async flow in Redux?
- . So it is just easier from the maintenance point of view to extract action creators into separate functions.
- Detail
- https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux
- https://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559
来源: https://juejin.im/post/5aa2a482f265da239611fcab