推荐入门学习链接:http://www.ruanyifeng.com/blog/2015/03/react.html
hooks学习链接:http://www.ruanyifeng.com/blog/2019/09/react-hooks.html

image.png

1.jsx条件知识点

  • 变量、表达式
  • class style
  • 子元素和组件

经常用就不仔细分析了

2.事件

  • bind this
  • 关于event参数
  • 传递自定义参数

1.为什么需要bind this?

我们先来看一段代码

  1. import React, { Component } from "react";
  2. class JsxBaseDemo extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. name: "小明",
  7. };
  8. }
  9. handleClick(){
  10. console.log('this....',this)
  11. this.setState({
  12. name:'大明'
  13. })
  14. }
  15. render() {
  16. return (
  17. <div>
  18. <span onClick={this.handleClick}>{this.state.name}</span>
  19. </div>
  20. );
  21. }
  22. }
  23. export default JsxBaseDemo;

image.png

可以看出this的值为undefined。

解决办法有两种,第一种采用bind this的方式将this绑定在实例上。
image.png

第二种采用静态方法使this指向实例。
image.png

2.关于event参数

先来看一段代码

  1. import React, { Component } from "react";
  2. class JsxBaseDemo extends Component {
  3. // 静态方法this指向实例
  4. handleClick = (event) =>{
  5. event.preventDefault() // 阻止默认行为
  6. console.log('target',event.target) //触发 指向当前元素
  7. console.log('currentTarget',event.currentTarget) // 绑定 指向当前元素,假象!!
  8. console.log('event',event)
  9. console.log('nativeEvent',event.nativeEvent) // 原生事件对象
  10. console.log('target',event.nativeEvent.target) // 触发
  11. console.log('currentTarget',event.nativeEvent.currentTarget) // 绑定
  12. }
  13. render() {
  14. return (
  15. <div>
  16. <a onClick={this.handleClick} href="http://www.baidu.com">跳转</a>
  17. </div>
  18. );
  19. }
  20. }
  21. export default JsxBaseDemo;

event.preventDefault() 阻止默认行为,使得a标签失去原有的能力。

console.log(‘currentTarget’,event.currentTarget)指向当前元素是假象!!

如何理解呢?我们来看看event事件的原型。 event.proto.constructor 。下图可以清晰的看到console.log(‘event’,event)的原型是一个组合事件,并不是原生MouseEvent的事件。image.png

真正的原生event事件 是console.log(‘nativeEvent’,event.nativeEvent)。
image.png
总结一下:

  • react的event实际上是SyntheticEvent(是一个组合事件并不是原生事件)模拟出dom事件的所有能力
  • event.nativeEvent是原生事件对象
  • 所有的事件都被挂载到document上
  • 和DOM事件不一样,和Vue事件也不一样

    3.传参
    1. handleClick = (id, event) => {
    2. event.preventDefault();
    3. console.log(id);
    4. console.log(event);
    5. };
    6. render() {
    7. return (
    8. <div>
    9. <a onClick={(event) => this.handleClick(1,event)} href="http://www.baidu.com"> //默认追加一个event参数
    10. 跳转
    11. </a>
    12. </div>
    13. );
    14. }

    3.表单

  • 受控组件

  • 非受控组件

    1.受控组件

    受state的控制 ```javascript class JsxBaseDemo extends Component { constructor(props) { super(props); this.state = {

    1. name: "lin",

    }; }

    handleChange = (e) => { this.setState({

    1. name: e.target.value,

    }); };

    render() { return (

    1. <div>
    2. <p>{this.state.name}</p>
    3. <label htmlFor="inputName">姓名:</label>
    4. <input type="text" id="inputName" onChange={this.handleChange} />
    5. </div>

    ); } }

export default JsxBaseDemo;

  1. <a name="60Pt4"></a>
  2. ##### 2.非受控组件
  3. 非受控组件在高阶组件中讲解
  4. <a name="mJiMG"></a>
  5. ### 4.组件使用
  6. - props传递数据
  7. - props类型检查
  8. - props传递函数
  9. ```javascript
  10. import React from 'react'
  11. import PropTypes from 'prop-types'
  12. class Input extends React.Component {
  13. constructor(props) {
  14. super(props)
  15. this.state = {
  16. title: ''
  17. }
  18. }
  19. render() {
  20. return <div>
  21. <input value={this.state.title} onChange={this.onTitleChange}/>
  22. <button onClick={this.onSubmit}>提交</button>
  23. </div>
  24. }
  25. onTitleChange = (e) => {
  26. this.setState({
  27. title: e.target.value
  28. })
  29. }
  30. onSubmit = () => {
  31. const { submitTitle } = this.props
  32. submitTitle(this.state.title) // 'abc'
  33. this.setState({
  34. title: ''
  35. })
  36. }
  37. }
  38. // props 类型检查
  39. Input.propTypes = {
  40. submitTitle: PropTypes.func.isRequired
  41. }
  42. class List extends React.Component {
  43. constructor(props) {
  44. super(props)
  45. }
  46. render() {
  47. const { list } = this.props
  48. return <ul>{list.map((item, index) => {
  49. return <li key={item.id}>
  50. <span>{item.title}</span>
  51. </li>
  52. })}</ul>
  53. }
  54. }
  55. // props 类型检查
  56. List.propTypes = {
  57. list: PropTypes.arrayOf(PropTypes.object).isRequired
  58. }
  59. class Footer extends React.Component {
  60. constructor(props) {
  61. super(props)
  62. }
  63. render() {
  64. return <p>
  65. {this.props.text}
  66. {this.props.length}
  67. </p>
  68. }
  69. componentDidUpdate() {
  70. console.log('footer did update')
  71. }
  72. shouldComponentUpdate(nextProps, nextState) {
  73. if (nextProps.text !== this.props.text
  74. || nextProps.length !== this.props.length) {
  75. return true // 可以渲染
  76. }
  77. return false // 不重复渲染
  78. }
  79. // React 默认:父组件有更新,子组件则无条件也更新!!!
  80. // 性能优化对于 React 更加重要!
  81. // SCU 一定要每次都用吗?—— 需要的时候才优化
  82. }
  83. class TodoListDemo extends React.Component {
  84. constructor(props) {
  85. super(props)
  86. // 状态(数据)提升
  87. this.state = {
  88. list: [
  89. {
  90. id: 'id-1',
  91. title: '标题1'
  92. },
  93. {
  94. id: 'id-2',
  95. title: '标题2'
  96. },
  97. {
  98. id: 'id-3',
  99. title: '标题3'
  100. }
  101. ],
  102. footerInfo: '底部文字'
  103. }
  104. }
  105. render() {
  106. return
  107. <div>
  108. <Input submitTitle={this.onSubmitTitle}/>
  109. <List list={this.state.list}/>
  110. <Footer text={this.state.footerInfo} length={this.state.list.length}/>
  111. </div>
  112. }
  113. onSubmitTitle = (title) => {
  114. this.setState({
  115. list: this.state.list.concat({
  116. id: `id-${Date.now()}`,
  117. title
  118. })
  119. })
  120. }
  121. }
  122. export default TodoListDemo

5.setState

  • 不可变值
  • 可能是异步更新
  • 可能会被合并

    1.不可变值

    ```javascript class StateDemo extends React.Component { constructor(props) {

    1. super(props)
    2. // 第一,state 要在构造函数中定义
    3. this.state = {
    4. count: 0
    5. }

    } render() {

    1. return <div>
    2. <p>{this.state.count}</p>
    3. <button onClick={this.increase}>累加</button>
    4. </div>

    } increase = () => {

    1. // 第二,不要直接修改 state ,使用不可变值 ----------------------------
    2. // this.state.count++ // 错误
    3. // this.setState({
    4. // count: this.state.count + 1 // 正确SCU
    5. // })
    6. // 操作数组、对象的的常用形式
    7. // -------------------------- 我是分割线 -----------------------------
    8. // 注意做setState修改值时
    9. // 不可变值(函数式编程,纯函数) - 数组 不要修改原数组
    10. // const list5Copy = this.state.list5.slice() // 类似深拷贝 不会影响原来的值
    11. // list5Copy.splice(2, 0, 'a') // 中间插入/删除
    12. // this.setState({
    13. // list1: this.state.list1.concat(100), // 追加
    14. // list2: [...this.state.list2, 100], // 追加
    15. // list3: this.state.list3.slice(0, 3), // 截取
    16. // list4: this.state.list4.filter(item => item > 100), // 筛选
    17. // list5: list5Copy // 其他操作
    18. // })
    19. // 注意,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值
    20. // 不可变值 - 对象
    21. // this.setState({
    22. // obj1: Object.assign({}, this.state.obj1, {a: 100}),
    23. // obj2: {...this.state.obj2, a: 100}
    24. // })
    25. // 注意,不能直接对 this.state.obj 进行属性设置,这样违反不可变值
    26. }

export default StateDemo

  1. 总结一下:不可变值就是需要在setState中设置state操作,不要提前做修改。如果提前修改要对原来的state值做下深拷贝,保证不影响原state中的值。虽然使用不规范不会造成错误,但是会影响性能和渲染。
  2. <a name="WjJdN"></a>
  3. ##### 2.可能是异步更新
  4. **1.场景一:直接用:异步**
  5. ```javascript
  6. import React, { Component } from "react";
  7. class JsxBaseDemo extends Component {
  8. constructor(props) {
  9. super(props);
  10. this.state = {
  11. count: 0,
  12. };
  13. }
  14. handleClick = (e) => {
  15. this.setState({
  16. count: this.state.count + 1,
  17. },() => {
  18. // 相当于vue中nextTick
  19. console.log('callback count',this.state.count) // 回调函数中可以拿到最新的 state
  20. });
  21. console.log('count',this.state.count) //异步的 拿不到最新的值
  22. };
  23. render() {
  24. return (
  25. <div>
  26. <p>{this.state.count}</p>
  27. <button onClick={this.handleClick}>追加</button>
  28. </div>
  29. );
  30. }
  31. }
  32. export default JsxBaseDemo;

结论:直接用,setState是异步的,拿不到最新的值,可以在回调函数中拿到最新的值
image.png
2.场景二:setTimeOut:同步

  1. import React, { Component } from "react";
  2. class JsxBaseDemo extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. count: 0,
  7. };
  8. }
  9. handleClick = (e) => {
  10. setTimeout(() => {
  11. this.setState({
  12. count: this.state.count + 1, // setTimeout中是同步的
  13. });
  14. console.log('count in setTimeout',this.state.count)
  15. },0)
  16. };
  17. render() {
  18. return (
  19. <div>
  20. <p>{this.state.count}</p>
  21. <button onClick={this.handleClick}>追加</button>
  22. </div>
  23. );
  24. }
  25. }
  26. export default JsxBaseDemo;

结论:setTimeout 中 setState 是同步的
image.png
3.场景三:自定义事件:同步

  1. import React, { Component } from "react";
  2. class JsxBaseDemo extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. count: 0,
  7. };
  8. }
  9. componentDidMount() {
  10. // 自己定义的 DOM 事件,setState 是同步的
  11. document.body.addEventListener("click", this.bodyClickHandler);
  12. }
  13. bodyClickHandler = () => {
  14. this.setState({
  15. count: this.state.count + 1,
  16. });
  17. console.log("count in body event", this.state.count);
  18. };
  19. componentWillMount() {
  20. // 及时销毁自定义 DOM事件
  21. document.body.removeEventListener("click", this.bodyClickHandler);
  22. }
  23. render() {
  24. return (
  25. <div>
  26. <p>{this.state.count}</p>
  27. <button onClick={this.handleClick}>追加</button>
  28. </div>
  29. );
  30. }
  31. }
  32. export default JsxBaseDemo;

结论:自定义事件setState是同步的。
image.png

3.可能被合并

1.传入对象会被合并
  1. import React, { Component } from "react";
  2. class JsxBaseDemo extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. count: 0,
  7. };
  8. }
  9. handleClick = () => {
  10. // 传入对象,会被合并(类似 Object.assign )。执行结果只一次 +1
  11. this.setState({
  12. count: this.state.count + 1,
  13. });
  14. this.setState({
  15. count: this.state.count + 1,
  16. });
  17. this.setState({
  18. count: this.state.count + 1,
  19. });
  20. };
  21. render() {
  22. return (
  23. <div>
  24. <p>{this.state.count}</p>
  25. <button onClick={this.handleClick}>追加</button>
  26. </div>
  27. );
  28. }
  29. }
  30. export default JsxBaseDemo;

image.png

2.传入函数不会被合并
  1. import React, { Component } from "react";
  2. class JsxBaseDemo extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. count: 0,
  7. };
  8. }
  9. handleClick = () => {
  10. // 传入函数,不会被合并。执行结果是 +3
  11. this.setState((prevState, props) => {
  12. return {
  13. count: prevState.count + 1,
  14. };
  15. });
  16. this.setState((prevState, props) => {
  17. return {
  18. count: prevState.count + 1,
  19. };
  20. });
  21. this.setState((prevState, props) => {
  22. return {
  23. count: prevState.count + 1,
  24. };
  25. });
  26. };
  27. render() {
  28. return (
  29. <div>
  30. <p>{this.state.count}</p>
  31. <button onClick={this.handleClick}>追加</button>
  32. </div>
  33. );
  34. }
  35. }
  36. export default JsxBaseDemo;

image.png

6.组件生命周期

image.png

1.constructor

  • 用于初始化内部状态,很少使用
  • 唯一可以修改state的地方


    2.getDervedStateFromProps

  • 当state需要从props初始化时使用

  • 尽量不要使用:维护两者状态一致性会增加复杂度
  • 每次render都会调用
  • 典型场景:表单控件获取默认值

3.componentDidMount

  • UI渲染完成后调用
  • 只执行一次
  • 典型场景:获取外部资源

4.componentWillUnMount

  • 组件移除时会被调用
  • 典型场景:资源释放

5.getSnapshotBeforeUpdate

  • 在页面render之前被调用,state已更新
  • 典型场景:获取render之前的DOM状态

6.componentDidUpdate

  • 每次UI更新时会调用
  • 典型场景:页面需要根据props变化重新获取数据

7.shouldComponentUpdate

  • 决定Virtual Dom是否重绘
  • 一般可以由PureComponent自动实现
  • 典型场景:性能优化