生命周期 
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
组件的生命周期可分成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
生命周期的方法有
- **componentWillMount **在渲染前调用,在客户端也在服务端。
- **componentDidMount **: 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
- **componentWillReceiveProps **在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
- **shouldComponentUpdate **返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。可以在你确认不需要更新组件时使用。
- componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
- **componentDidUpdate **在组件完成更新后立即调用。在初始化时不会被调用。
- componentWillUnmount在组件从 DOM 中移除之前立刻被调用。
hooks 
userStateuseEffectuseContext
useContext可以帮助我们跨越组件层级直接传递变量,实现共享。 需要注意的是useContext和redux的作用是不同的!!!
useContext:解决的是组件之间值传递的问题
redux:是应用中统一管理状态的问题
但通过和useReducer的配合使用,可以实现类似Redux的作用。
useReducer
useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
useCallback
返回一个memoized回调函数,我的理解即返回一个函数的句柄,等同于函数的变量,因此你可以使用memoizedCallback()进行执行该函数或者传递给事件和子组件,这里可以推荐绝大多数事件或者子组件的方法使用useCallback,避免组件更新重复渲染。 因此useCallback中的doSomething并不会在定义时就执行,而是需要手动调用返回的memoizedCallback才是真的执行。简单理解为useCallback定义了一个函数,仅在deps发生变化时重新定义该函数,否则该函数的变量不会变化,事件和子组件内容也就不用重新绑定或者渲染。
useMemo
作为性能优化的手段(缓存数据) 原理与 class 组件的 PureComponent 优化原理相同,对 props 进行了浅比较
useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。常见访问子组件
useImperativeMethodsuseMutationEffectuseLayoutEffect
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
useEffect 和 useLayoutEffect 的区别? 
useEffect 在渲染时是异步执行,并且要等到浏览器将所有变化渲染到屏幕后才会被执行。 useLayoutEffect 在渲染时是同步执行,其执行时机与 componentDidMount,componentDidUpdate 一致
对于 useEffect 和 useLayoutEffect 哪一个与 componentDidMount,componentDidUpdate 的是等价的? 
useLayoutEffect,因为从源码中调用的位置来看,useLayoutEffect的 create 函数的调用位置、时机都和 componentDidMount,componentDidUpdate 一致,且都是被 React 同步调用,都会阻塞浏览器渲染。
useEffect 和 useLayoutEffect 哪一个与 componentWillUnmount 的是等价的? 
同上,useLayoutEffect 的 detroy 函数的调用位置、时机与 componentWillUnmount 一致,且都是同步调用。useEffect 的 detroy 函数从调用时机上来看,更像是 componentDidUnmount (注意React 中并没有这个生命周期函数)。
为什么建议将修改 DOM 的操作里放到 useLayoutEffect 里,而不是 useEffect? 
可以看到在流程9/10期间,DOM 已经被修改,但但浏览器渲染线程依旧处于被阻塞阶段,所以还没有发生回流、重绘过程。由于内存中的 DOM 已经被修改,通过 useLayoutEffect 可以拿到最新的 DOM 节点,并且在此时对 DOM 进行样式上的修改,假设修改了元素的 height,这些修改会在步骤 11 和 react 做出的更改一起被一次性渲染到屏幕上,依旧只有一次回流、重绘的代价。 如果放在 useEffect 里,useEffect 的函数会在组件渲染到屏幕之后执行,此时对 DOM 进行修改,会触发浏览器再次进行回流、重绘,增加了性能上的损耗。
函数式编程 
编程范式 纯函数 不可变值
JSX 本质 和 vdom 
JSX 即 createElement 函数 执行生成 vnode patch(elem, vnode) patch(vnode, newVnode)
合成事件 
所有事件挂载到 document 上 event 不是原生的,是 SyntheticEvent 合成事件对象,模拟出来 DOM 事件所有能力 所有的事件,都被挂载到 document 上 和 Vue 事件不同,和 Dom 事件也不同
import React from 'react'
class EventDemo extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: 'zhangsan',
      list: [
        {
          id: 'id-1',
          title: '标题1',
        },
        {
          id: 'id-2',
          title: '标题2',
        },
        {
          id: 'id-3',
          title: '标题3',
        },
      ],
    }
    // 修改方法的 this 指向
    this.clickHandler1 = this.clickHandler1.bind(this)
  }
  render() {
    // this - 使用 bind
    // return <p onClick={this.clickHandler1}>{this.state.name}</p>;
    // this - 使用静态方法
    // return <p onClick={this.clickHandler2}>clickHandler2 {this.state.name}</p>;
    // event
    // return (      ,,,
    // 	<a href="https://imooc.com/" onClick={this.clickHandler3}>
    // 		click me
    // 	</a>
    // );
    // 传递参数 - 用 bind(this, a, b)
    return (
      <ul>
        {this.state.list.map((item, index) => {
          return (
            <li
              key={item.id}
              onClick={this.clickHandler4.bind(this, item.id, item.title)}
            >
              index {index}; title {item.title}
            </li>
          )
        })}
      </ul>
    )
  }
  clickHandler1() {
    // console.log('this....', this) // this 默认是 undefined
    this.setState({
      name: 'lisi',
    })
  }
  // 静态方法,this 指向当前实例
  clickHandler2 = () => {
    this.setState({
      name: 'lisi',
    })
  }
  // 获取 event
  clickHandler3 = (event) => {
    event.preventDefault() // 阻止默认行为
    event.stopPropagation() // 阻止冒泡
    console.log('target', event.target) // 指向当前元素,即当前元素触发
    console.log('current target', event.currentTarget) // 指向当前元素,假象!!!
    // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件
    console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent
    console.log('event.__proto__.constructor', event.__proto__.constructor)
    // 原生 event 如下。其 __proto__.constructor 是 MouseEvent
    console.log('nativeEvent', event.nativeEvent)
    console.log('nativeEvent target', event.nativeEvent.target) // 指向当前元素,即当前元素触发
    console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!
    // 1. event 是 SyntheticEvent ,模拟出来 DOM 事件所有能力
    // 2. event.nativeEvent 是原生事件对象
    // 3. 所有的事件,都被挂载到 document 上
    // 4. 和 DOM 事件不一样,和 Vue 事件也不一样
  }
  // 传递参数
  clickHandler4(id, title, event) {
    console.log(id, title)
    console.log('event', event) // 最后追加一个参数,即可接收 event
  }
}
export default EventDemo为何要合成事件机制 
更好的兼容性和跨平台 载到 document ,减少内存消化,避免频繁解绑 方便事件的统一管理(如事务机制)

setState 和 batchUpdate 
有时异步(普通使用),有时同步(setTimeout、DOM事件) 有时合并(对象形式),有时不合并(函数形式) 后者比较好理解(像Object.assign),主要讲解前者
import React from 'react'
// 函数组件(后面会讲),默认没有 state
class StateDemo extends React.Component {
  constructor(props) {
    super(props)
    // 第一,state 要在构造函数中定义
    this.state = {
      count: 0,
    }
  }
  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.increase}>累加</button>
      </div>
    )
  }
  increase = () => {
    // 第二,不要直接修改 state ,使用不可变值 ----------------------------
    // this.state.count++ // 错误
    // this.setState({
    // 	count: this.state.count + 1, // SCU
    // });
    // 操作数组、对象的的常用形式
    // 第三,setState 可能是异步更新(有可能是同步更新) ----------------------------
    // this.setState(
    // 	{
    // 		count: this.state.count + 1,
    // 	},
    // 	() => {
    // 		// 联想 Vue $nextTick - DOM
    // 		console.log("count by callback", this.state.count); // 回调函数中可以拿到最新的 state
    // 	}
    // );
    // console.log("count", this.state.count); // 异步的,拿不到最新值
    // // setTimeout 中 setState 是同步的
    // setTimeout(() => {
    // 	this.setState({
    // 		count: this.state.count + 1,
    // 	});
    // 	console.log("count in setTimeout", this.state.count);
    // }, 0);
    // 自己定义的 DOM 事件,setState 是同步的。再 componentDidMount 中
    // 第四,state 异步更新的话,更新前会被合并 ----------------------------
    // 传入对象,会被合并(类似 Object.assign )。执行结果只一次 +1
    // this.setState({
    // 	count: this.state.count + 1,
    // });
    // this.setState({
    // 	count: this.state.count + 1,
    // });
    // this.setState({
    // 	count: this.state.count + 1,
    // });
    // 传入函数,不会被合并。执行结果是 +3
    this.setState((prevState, props) => {
      return {
        count: prevState.count + 1,
      }
    })
    this.setState((prevState, props) => {
      return {
        count: prevState.count + 1,
      }
    })
    this.setState((prevState, props) => {
      return {
        count: prevState.count + 1,
      }
    })
  }
  // bodyClickHandler = () => {
  // 	this.setState({
  // 		count: this.state.count + 1,
  // 	});
  // 	console.log("count in body event", this.state.count);
  // };
  // componentDidMount() {
  // 	// 自己定义的 DOM 事件,setState 是同步的
  // 	document.body.addEventListener("click", this.bodyClickHandler);
  // }
  // componentWillUnmount() {
  // 	// 及时销毁自定义 DOM 事件
  // 	document.body.removeEventListener("click", this.bodyClickHandler);
  // 	// clearTimeout
  // }
}
export default StateDemo
// -------------------------- 我是分割线 -----------------------------
// // 不可变值(函数式编程,纯函数) - 数组
// const list5Copy = this.state.list5.slice();
// list5Copy.splice(2, 0, "a"); // 中间插入/删除
// this.setState({
// 	list1: this.state.list1.concat(100), // 追加
// 	list2: [...this.state.list2, 100], // 追加
// 	list3: this.state.list3.slice(0, 3), // 截取
// 	list4: this.state.list4.filter((item) => item > 100), // 筛选
// 	list5: list5Copy, // 其他操作
// });
// // 注意,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值
// // 不可变值 - 对象
// this.setState({
// 	obj1: Object.assign({}, this.state.obj1, { a: 100 }),
// 	obj2: { ...this.state.obj2, a: 100 },
// });
// // 注意,不能直接对 this.state.obj 进行属性设置,这样违反不可变值
this.setState异步还是同步? 
setState 无所谓异步还是同步 看是否能命中 batchedUpdates 机制, Legacy模式命中异步,反之同步;Concurrent模式统一异步 判断 isBatchingUpdates
例:
- 合成事件中是异步
- 钩子函数中的是异步
- 原生事件中是同步
- setTimeout中是同步
哪些能命中 batchUpdate 机制 
生命周期(和它调用的函数) React 中注册的事件(和它调用的函数) React 可以 “管理”的入口
哪些不能命中 batchUpdate 机制 
setTimeout setInterval等(和它调用的函数) 自定义的 DOM 事件(和它调用的函数) React “管不到”的入口
transaction 事务机制 
组件渲染和更新过程 
JSX 如何渲染成页面 setState 之后如何更新页面 全流程 更新的两个阶段:reconciliation commit React fiber
表单校验 
受控组件(非受控组件) input textarea select 用 value checkbox radio 用 checked
import React, { memo } from 'react'
import { Form, Input } from 'antd'
const Test = memo((props) => {
  const [form] = Form.useForm()
  const passwordValidator = (rule, value, callback) => {
    const { getFieldValue } = form
    if (value && value !== getFieldValue('password')) {
      callback('两次输入不一致!')
    }
    callback()
  }
  return (
    <Form from={form}>
      <Form.Item label='非空限制'>
        {getFieldDecorator('name', {
          rules: [
            {
              required: true,
              message: '不能为空',
            },
          ],
        })(<Input placeholder='请输入名称' />)}
      </Form.Item>
      <Form.Item label='字符串限制-范围限制'>
        {getFieldDecorator('password', {
          rules: [
            {
              required: true,
              message: '密码不能为空',
            },
            {
              min: 4,
              message: '密码不能少于4个字符',
            },
            {
              max: 6,
              message: '密码不能大于6个字符',
            },
          ],
        })(<Input placeholder='请输入密码' type='password' />)}
      </Form.Item>
      <Form.Item label='字符串限制-长度限制'>
        {getFieldDecorator('nickname', {
          rules: [
            {
              required: true,
              message: '昵称不能为空',
            },
            {
              len: 4,
              message: '长度需4个字符',
            },
          ],
        })(<Input placeholder='请输入昵称' />)}
      </Form.Item>
      <Form.Item label='自定义校验'>
        {getFieldDecorator('passwordcomfire', {
          rules: [
            {
              required: true,
              message: '请再次输入密码',
            },
            {
              validator: passwordValidator,
            },
          ],
        })(<Input placeholder='请输入密码' type='password' />)}
      </Form.Item>
      <Form.Item label='空格校验'>
        {getFieldDecorator('hobody', {
          rules: [
            {
              whitespace: true,
              message: '不能输入空格',
            },
          ],
        })(<Input placeholder='请输入昵称' />)}
      </Form.Item>
      <Form.Item label='正则校验'>
        {getFieldDecorator('qbc', {
          rules: [
            {
              message: '只能输入数字',
              pattern: /^[0-9]+$/,
            },
          ],
        })(<Input placeholder='请输入ABC' />)}
      </Form.Item>
    </Form>
  )
})
export default Test组件使用 
props 传递数据 props 传递函数 props 类型检查
异步加载 
import ReactDOM from 'react-dom'
import React, { Component, lazy, Suspense } from 'react'
const Sub = lazy(() => import('./sub'))
class App extends Component {
  render() {
    return (
      <div>
        <Suspense fallback={<div>loading</div>}>
          <Sub />
        </Suspense>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))组件优化-经典面试题 
import React, { Component, PureComponent, memo } from 'react'
import ReactDOM from 'react-dom'
// 组件的优化
class Sub extends PureComponent {
  // shouldComponentUpdate(nextProps, nextState) {
  // 	if (nextProps.name === this.props.name) {
  // 		return false;
  // 	}
  // 	return true;
  // }
  render() {
    console.log('sub render')
    return <div>sub</div>
  }
}
const SubFun = memo((props) => {
  console.log('SubFun render')
  return <div>SubFun</div>
})
class App extends Component {
  state = {
    count: 0,
  }
  handleClick = () => {
    this.setState({
      count: this.state.count + 1,
    })
  }
  callback = () => {}
  render() {
    console.log('render')
    const { count } = this.state
    return (
      <div>
        {/* <Sub name="wcd" /> */}
        {/* <Sub cb={() => this.callback()} /> */}
        <Sub cb={this.callback} />
        <SubFun cb={this.callback} />
        <p>{count}</p>
        <button onClick={this.handleClick}>button</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))React.FC 
经常出现React.Fc这个函数, 比如我不使用React.Fc来处理组件的函数, 则在组件里面使用props.children会报错, 我们一起进入源码分析一下。
type FC<P = {}> = FunctionComponent<P>
interface FunctionComponent<P = {}> {
  (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null
  propTypes?: WeakValidationMap<P> | undefined
  contextTypes?: ValidationMap<any> | undefined
  defaultProps?: Partial<P> | undefined
  displayName?: string | undefined
}- FC这个- type接收一个参数- P, 默认值为空对象, 而这个- P。
- FunctionComponent就是个过度的名称而已, 可以认为- FC就是- FunctionComponent。
- 第一句意义为第一个参数为 PropsWithChildren<P>类型, 第二个参数可有可无, 有则为任意类型, 返回React的dom或者返回null。
- 后面四个参数不是必填, 我们主要研究第一句。
我们追查一下 PropsWithChildren
type PropsWithChildren<P> = P & { children?: ReactNode | undefined }只是将传入的P的类型与{ children?: ReactNode | undefined } 合并而已, 看到这里我们就明白了, 其实用 React.FC 包裹一下是可以帮助 ts 推导出 props 身上可能有 children 属性。
HOC 
高阶组件就是一个函数,接受一个组件作为参数,返回一个新的组件
