React学习笔记

402 阅读6分钟

概述

声明式编码

React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React 能高效更新并渲染合适的组件。

组件化

使用

安装

可以通过CDN引入

可以通过下载为本地包进行引入

HelloReact的开始

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <script src="../react_js/babel.min.js"></script>
  <script crossorigin src="../react_js/react.development.js"></script>
  <script crossorigin src="../react_js/react-dom.development.js"></script>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const VDOM = <h1>Hello_React</h1>;
      ReactDOM.render(VDOM, document.getElementById("root"));
    </script>
  </body>
</html>

JSX语法的相关规则

react定义的一种类似于XML的JS扩展语法: JS + XML本质是**React.createElement(component, props, ...children)**方法的语法糖

1、定义虚拟DOM时,不要写引号

2、标签中混入JS表达式时要用{ }

3、样式的类名不要用class,用className

4、内联样式,要用style={{key:value}}的形式去写

5、只有一个根标签

6、标签首字母

(1)若小写字母开头,比如,则将标签转为html同名元素,若html中无该标签对应的同名元素,则报错

(2)若大写字母开头,react就去渲染对应的组件

第一个函数式组件

<body>
  <div id="root"></div>
  <script type="text/babel">
    //1、函数式组件
    function MyComponent() {
      return <div>Hello,I am Lin</div>;
    }
    ReactDOM.render(<MyComponent />, document.getElementById("root"));
  </script>
</body>

注意:函数名称要大写

执行了ReactDOM.render(,...之后发生了什么?

1、React解析组件标签,找到了MyComponent组件

2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转换为真实DOM,随后展示在页面中

第一个类式组件

<body>
  <div id="root"></div>
  <script type="text/babel">
    //1、函数式组件
    function MyComponent() {
      return <div>Hello,I am Lin</div>;
    }
    //2.类式组件
    class HelloComponent extends React.Component {
      render() {
        return <h2>我是类式组件</h2>;
      }
    }
    ReactDOM.render(<HelloComponent />, document.getElementById("root"));
  </script>
</body>

执行了ReactDOM.render(,....之后发生了什么?

1、React解析组件标签,找到了MyComponent组件

2、发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法

3、将render返回的虚拟DOM转换为真实DOM,随后呈现在页面中。

三大属性——state

<body>
  <div id="root"></div>
  <script type="text/babel">
    class HelloComponent extends React.Component {
      constructor(props) {
        super(props);
        this.state = { isHot: true };
      }
      changeWeather = () => {
        console.log("hhh");
        this.setState({ isHot: !this.state.isHot });
      };
      render() {
        return (
          <h2 onClick={this.changeWeather}>
            今天天气很{this.state.isHot ? "炎热" : "寒冷"}
          </h2>
        );
      }
    }
    ReactDOM.render(<HelloComponent />, document.getElementById("root"));
  </script>
</body>

React 的核心思想是组件化,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。

一般是在react组件中的constructor中写一个this.state,是一个对象

      constructor(props) {
        super(props);
        this.state = { isHot: true };
      }

1、用setState 修改State

2、State 的更新是异步的

  • 调用setState后,setState会把要修改的状态放入一个队列中(因而 组件的state并不会立即改变);
  • 之后React 会优化真正的执行时机,来优化性能,所以优化过程中有可能会将多个 setState 的状态修改合并为一次状态修改,因而state更新可能是异步的。
  • 所以不要依赖当前的State,计算下个State。当真正执行状态修改时,依赖的this.state并不能保证是最新的State,因为React会把多次State的修改合并成一次,这时,this.state将还是这几次State修改前的State。

3、State更新会被合并

4、setState里顺序更新

三大属性——props

<body>
  <div id="root"></div>
  <script type="text/babel">
    class HelloComponent extends React.Component {
      state = {};
      constructor(props) {
        super(props);
      }
      render() {
        const { name } = this.props;
        return <div>{name}</div>;
      }
    }
    ReactDOM.render(
      <HelloComponent name="Hello" />,
      document.getElementById("root")
    );
  </script>
</body>


<body>
  <div id="root"></div>
  <script type="text/babel">
    function Person(props) {
      const { name, age } = props;
      return (
        <h2>
          {name}---{age}
        </h2>
      );
    }
    ReactDOM.render(
      <Person name="lin" age="20" />,
      document.getElementById("root")
    );
  </script>
</body>

我们可以对props传入的类型做一个限制,和一个默认值

<body>
  <div id="root"></div>
  <script type="text/babel">
    function Person(props) {
      const { name, age } = props;
      return (
        <h2>
          {name}---{age}
        </h2>
      );
    }
    Person.propTypes = {
      name: PropTypes.string.isRequired,
      age: PropTypes.number,
    };
    Person.defaultProps = {
      age: 20,
    };
    let person = {
      name: "lin",
      age: "20",
    };
    ReactDOM.render(<Person {...person} />, document.getElementById("root"));
  </script>
</body>

我们需要提前引入propTypes的包,然后进行使用

三大属性——refs

1、字符串形式的ref

<body>
  <div id="root"></div>
  <script type="text/babel">
    class Demo extends React.Component {
      showData = () => {
        console.log(this.refs.input1.value);
      };
      render() {
        return (
          <div>
            <input type="text" ref="input1" />
            <button onClick={this.showData}>点我提示左侧信息</button>
            <input type="text" placeholder="失焦提示信息" ref="input2" onBlur={this.showData}/>
          </div>
        );
      }
    }

    ReactDOM.render(<Demo/>, document.getElementById("root"));
  </script>
</body>

2、回调函数形式的ref

<body>
  <div id="root"></div>
  <script type="text/babel">
    class Demo extends React.Component {
      showData = () => {
        console.log(this.input1.value);
      };
      render() {
        return (
          <div>
            <input
              type="text"
              ref={(c) => {
                this.input1 = c;
              }}
            />
            <button onClick={this.showData}>点我提示左侧信息</button>
          </div>
        );
      }
    }

    ReactDOM.render(<Demo />, document.getElementById("root"));
  </script>
</body>

要注意:回调函数的形式的ref是挂载在实例本身,而不是在refs上的哦

3、createRef创建的ref容器

<body>
  <div id="root"></div>
  <script type="text/babel">
    class Demo extends React.Component {
      MyRef = React.createRef();
      showData = () => {
        console.log(this.MyRef.current.value);
      };
      render() {
        return (
          <div>
            <input type="text" ref={this.MyRef} />
            <button onClick={this.showData}>点我提示左侧信息</button>
          </div>
        );
      }
    }

    ReactDOM.render(<Demo />, document.getElementById("root"));
  </script>
</body>

事件处理

1. 通过onXxx属性指定事件处理函数(注意大小写)

  1. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件

  2. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)

2. 通过event.target得到发生事件的DOM元素对象

生命周期

旧版本的生命周期

1. 挂载卸载过程

1.1.constructor()

constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。

注意:只要使用了constructor()就必须写super(),否则会导致this指向错误。

1.2.componentWillMount()

componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。

1.3.componentDidMount()

组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,开启定时器,返回数据setState后组件会重新渲染

1.4.componentWillUnmount ()

在此处完成组件的卸载和数据的销毁。

  1. clear你在组件中所有的setTimeout,setInterval

  2. 移除所有组件中的监听 removeEventListener

  3. 有时候我们会碰到这个warning:

    Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.

原因:因为你在组件中的ajax请求返回setState,而你组件销毁的时候,请求还未完成,因此会报warning

解决方法:

componentDidMount() { 
    this.isMount === true 
    axios.post().then((res) => { 
        this.isMount && this.setState({ // 增加条件ismount为true时 aaa:res }) }) } 
componentWillUnmount() { 
    this.isMount === false 
}

2、更新过程

2.1. componentWillReceiveProps (nextProps)
  • 在接受父组件改变后的props需要重新渲染组件时用到的比较多
  • 接受一个参数nextProps
  • 通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件
2.2.shouldComponentUpdate(nextProps,nextState)——阀门
  1. 主要用于性能优化(部分更新)
  2. 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
  3. 因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断
2.3.componentWillUpdate (nextProps,nextState)

shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。

2.4.componentDidUpdate(prevProps,prevState)

组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

2.5.render()

render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。

新版本的生命周期

3.1.getDerivedStateFromProps

getDerivedStateFromProps本来(React v16.3中)是只在创建和更新(由父组件引发部分)中调用。如果不是由父组件引发,那么getDerivedStateFromProps也不会被调用,如自身setState引发或者forceUpdate引发。

3.2. getSnapshotBeforeUpdate(prevProps, prevState)

代替componentWillUpdate。

常见的 componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。

这两者的区别在于:

  1. 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在

componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。

  1. getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。

此生命周期返回的任何值都将作为参数传递给componentDidUpdate()

体会