• React 基础笔记


    概览

    React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript库。可以将一些简短、独立的代码片段组合成复杂的UI界面,这些片段被称为“组件”。

    React 大体包含下面这些概念:

    • 组件
    • JSX
    • Virtual DOM
    • Data Flow

    组件

    可以将UI 拆分为独立且复用的代码片段,每部分都可独立维护。
    组件,从概念上类似于 JavaScript 函数。它接受任意的参数(即 “props”),并返回用于描述页面展示内容的React 元素
    自定义组件命名:必须以大写字母开头,React 会将以小写字母开头的组件视为原生DOM标签。

    import React from 'react'; // React 的核心库
    import ReactDOM from 'react-dom'; // 提供与 DOM 相关的功能
    class ShoppingList extends React.Component {
      render() {
        return (
          <div className="shopping-list">
            <h1>Shopping List for {this.props.name}</h1>
            <ul>
              <li>Instagram</li>
              <li>WhatsApp</li>
              <li>Oculus</li>
            </ul>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <ShoppingList name="Mark" />,
      document.getElementById('root')
    );
    
    

    React 应用都是构建在组件之上。ShoppingList就是一个React 组件类型,ReactDOM.render 函数会将组件方到页面上的某个节点元素中。(render 返回了一个 React 元素 ,这是一种对渲染内容的轻量级描述。)
    大多数 React 应用只会调用一次 ReactDOM.render()

    其中props(是 properties 的简写) 是组件包含的两个核心概念之一,另一个是state

    props

    props接收一些其他组件的参数(比如上方的 name ),来配置组件,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改

    state

    state 来实现所谓“记忆”的功能。可以通过 React 组件的构造函数中设置 this.state;this.state 应该被视为一个组件的私有属性。
    修改this.state值需要通过this.setState方法赋值,有些 props 值或 state 值可能是异步更新的,使用对象赋值的方式更改 state 可能无效,可使用回调传参方式更新

    this.setState(  (state,props)=> ({count:state.count + props.count}) );
    

    类组件

    通过 class 语法来定义组件,必须包含render() 方法,并且继承于 React.Component
    类组件必须包含render(),并且return 只能返回一个父元素(类似vue中的template中必须要有一个父元素)。

    class Square extends React.Component {
      /**
       * @name constructor
       * @param {*} props
       * 每次定义子类的构造函数时,都必须调用 super 方法。
       * 因此,在所有含有构造函数的React组件中,构造函数必须以super(props)开头
       * state 保存着组件中的数据 类似 vue 中的 data 属性
       */
      constructor(props) {
        super(props);
        this.state = {
          value:null
        };
      }
      render() {
        return (
          <button className="square" onClick = {()=>{this.setState({value:'X'})}
          }>
            {this.state.value}
          </button>
        );
      }
    }
    

    render方法中的onClick 事件监听函数中调用this.setState方法,可以设置this.state 中的属性
    推荐使用箭头函数,避免this 造成困扰

    简单组件(函数组件)

    简单组件是一个函数,不需要使用class关键字,当然就没有constructor和state

    const Square =  (props) => {
     return  (<button className= "square"
       onClick= { props.onClick} >{ props.value  }
      </button>)
    }
    

    受控组件

    input<select><textarea> 等表单的状态发生改变,都得同时通过onChange事件改变组件中的state值,否则表单不会发生变化。通过这种方式控制取值的表单叫做受控组件

    class Input extends Component {
      constructor(props){
        super(props)
        this.state = {
          value:'3s'
        }
      }
      render (){
        return <input type="text" value = {this.state.value} />   // 用户在表单中输入任何信息都是无效的
      }
    }
    
    // 使用事件来改变
     render (){
        return (
          <input
              type="text"
              value = {this.state.value}
              onChange = {({target}) =>{
                this.setState({
                  value:target.value
                })
              }}
        />
        )
      }
    
    

    React受控组件更新state的流程:

    • 通过在初始化state中设置表单默认值;
    • 每当表单值发生变化时,调用onChange事件
    • 事件通过合成的事件对象来改变状态更新 state
    • setState触发视图渲染更新,完成表单组件值的更新

    渲染多个组件

    将组件变成数组集合放入花括号中即可渲染多个组件,通常使用数组的map()方法

    const Lis = (props) => {
        const lis = props.list.map((val,key)=> <li key={key}>{key+1}</li>);
        return <ul>{lis}</ul>
    }
    const list = Array(7).fill(null);
    ReactDOM.render(
      <Lis list = {list} />,
      document.getElementById('root')
    );
    

    状态提升

    当多个组件发生数据联动时,建议将共享状态提升到最近的共同父组件中去。

    /*
     * @Author: Owen
     * @Date: 2019-07-23 23:55:17
     * @Last Modified by: Owen
     * @Last Modified time: 2019-07-29 16:06:22
     */
    
    
    import React,{Component} from 'react';
    import {render} from 'react-dom';
    
    // 温度转化器
    let toConvert = (temperature,callback) => {
      let num = parseInt(temperature);
      if(Number.isNaN(num)) return ''
       num = callback(num)
      return Math.round(num*1000)/1000;
    }
    
    const BoilingVerdict = (props) =>{
      let text = props.temperature > 100?'':' not';
      return (<p>
        The water would{text} boil.
      </p>)
    }
    
    // 公共input组件只接收行为和状态
    const TemperatureInput = (props) =>{
      return (
        <input
        value ={props.temperature}
        onChange = {props.valueChange}
        />
      )
    }
    // 父组件设置行为和状态
    class Calculator extends Component {
      constructor(props){
        super(props)
        this.state = {
          temperature:'',
          scale:'C'
        }
      }
      toFahrenheit({target}) {
        this.setState({
          temperature:target.value,
          scale:'F'
        })
      }
      toCelsius({target}) {
        this.setState({
          temperature:target.value,
          scale:'C'
        })
      }
      render() {
        let {temperature,scale} = this.state;
        let celsius = scale === 'F'?toConvert(temperature,(val)=>(val - 32)*5/9):temperature;
        let fahrenheit = scale === 'C'?toConvert(temperature,(val)=>val*9/5+32):temperature;
    
        return (
          <div>
            <div>
              Celsius:
              <TemperatureInput
              scale ='C'
              temperature = {celsius}
              valueChange = {this.toCelsius.bind(this)}
            />
            </div>
    
            <div>
              Fahrenheit:
              <TemperatureInput
              scale ='F'
              temperature = {fahrenheit}
              valueChange = {this.toFahrenheit.bind(this)}
            />
            </div>
            <div><BoilingVerdict temperature = {this.state.temperature} /></div>
          </div>
    
        );
    
      }
    }
    render(<Calculator />,document.querySelector('#root'))
    

    在就React 应用中,任何科比数据应当只有一个相对应的数据源,通常,多个组件需要相同数据,可以将数据提升到这些组件的共同父组件中。依靠自上而下的数据流,去控制组件,而不是尝试在不同组件同步 state这样会减少将来排查和隔离BUG所需要的工作量

    组合(类似vue中的 slot)

    有些组件无法提前知晓它们子组件的具体内容,需要留坑,那么也可以通过 props来占位。

    默认坑位props.children

    件起始标签和结束标签之间的内容都会被将{props.children}替换。

    function FancyBorder(props) {
      return (
        <div className={'FancyBorder FancyBorder-' + props.color}>
          {props.children}
        </div>
      );
    }
    function WelcomeDialog() {
      return (
        <FancyBorder color="blue">
          <h1 className="Dialog-title">
            Welcome
          </h1>
          <p className="Dialog-message">
            Thank you for visiting our spacecraft!
          </p>
        </FancyBorder>
      );
    }
    

    自定义坑位

    因为 React元素 本质就是对象,所以可以将它当中参数像其他数据一样传递。

    function SplitPane(props) {
      return (
        <div className="SplitPane">
          <div className="SplitPane-left">
            {props.left}
          </div>
          <div className="SplitPane-right">
            {props.right}
          </div>
        </div>
      );
    }
    
    function App() {
      return (
        <SplitPane
          left={
            <Contacts />
          }
          right={
            <Chat />
          } />
      );
    }
    
    

    JSX

    每个 JSX 元素都是调用 React.createElement() 的语法糖。一般来说,如果你使用了 JSX,就不再需要调用createElement()createFactory()方法。
    React 提出的一种叫 JSX 的语法,这应该是最开始接触 React 最不能接受的设定之一,因为前端被“表现和逻辑层分离”这种思想“洗脑”太久了。实际上组件的 HTML 是组成一个组件不可分割的一部分,能够将 HTML 封装起来才是组件的完全体.

    JSX是一个JavaScript语法扩展。它类似于模板语言,但它具有JavaScript 的全部能力。它最终会被编译为React.createElement()函数调用,返回称为 React元素的普通JavaScript`对象。

    推荐使用箭头函数,避免this 造成困扰

    
     function Square(props) {
      return (
        < button className = "square"
                 onClick = { props.onClick } >
          { props.value }
        </button>
      );
    }
    
    class Board extends React.Component {
      constructor(props){
        super(props)
        this.state = {
          squares: Array(9).fill(null),
          xIsNext:true, // 先落子,并确认该哪位玩家落子
        }
      }
      /**
       * 只接受一个squares副本,而不直接修改本身数据
       * 1. 这样可以简化复杂的功能,不可变性使得复杂的特性更容易实现。
       * 2. 可以跟踪数据的改变,如果直接修改源数据就很难跟踪变化的数据。
       * 3. 可以帮助我们在 React 中创建 purecomponents。可以轻松的确定不可变数据是否发生了改变,
       *    从而确定何时对组件进行重新渲染。
       * @param {*} i
       * @memberof Board
       */
      handleClick(i) {
        const squares = this.state.squares.slice();
        squares[i] = this.state.xIsNext? "X":"O";
        this.setState({ squares,xIsNext:!this.state.xIsNext })
      }
    
      renderSquare(i) { // 返回一个 Square 组件
        return ( < Square
                  value = { this.state.squares[i] }// 给子组件传递 value数据
                  onClick = {()=> this.handleClick(i)} // 给子组件传递 onClick事件
            />);
      }
    
    
      render() {
        let {state} = this;
        const status = `Next player: ${state.xIsNext?'X':'O'}`;
    
        return (
          <div>
            <div className="status">{status}</div>
            <div className="board-row">
              {this.renderSquare(0)}
              {this.renderSquare(1)}
              {this.renderSquare(2)}
            </div>
            <div className="board-row">
              {this.renderSquare(3)}
              {this.renderSquare(4)}
              {this.renderSquare(5)}
            </div>
            <div className="board-row">
              {this.renderSquare(6)}
              {this.renderSquare(7)}
              {this.renderSquare(8)}
            </div>
          </div>
        );
      }
    }
    
    

    在 JSX 中你可以任意使用JavaScript表达式,只需要用一个大括号({})括起来;
    事实上每个 React 元素都是一个JavaScript 对象,可以把它保存在变量中或者作为参数传递。

    为避免遇到自动插入分号陷阱,最好将内容包裹在小括号中,如果只有一行代码则不需要括号

    // 加括号
    class App extends React.Component {
      render() {
        return (
          <div className="shopping-list">
            <h1>Shopping List for</h1>
            <ul>
              <li>Instagram</li>
              <li>WhatsApp</li>
              <li>Oculus</li>
            </ul>
          </div>
        );
      }
    }
    
    // 不用加括号
    class App extends React.Component {
      render() {
        return  <li>Instagram</li>;
      }
    }
    

    JSX 语法更接近于 JavaScript,所以 ReactDom 使用cameCase(小驼峰命名)来定义属性名称,并且不要使用引号将大括号包裹,两者是不能并存的。对于字符串值使用引号,对于表达式使用大括号

    React 中无法通过 return false 的方式阻止默认行为,必须使用e.preventDefault()阻止默认事件。但是不用担心event事件的兼容问题

    JSX 本身就能防止XSS攻击

    // 原生DOM
    <a href="#" onclick="console.log('The link was clicked.'); return false">
      Click me
    </a>
    
    // JSX
    class App extends React.Component {
      render() {
        return (
            <a href="#" onclick={(e)=>this.handleClick(e)}> //每次渲染时都会创建不同的回调函数。该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。
                Click me
            </a>;
            <a href="#" onclick={(e)=>this.handleClick(id,e)}> // 向事件处理程序传递参数
                Click me
            </a>;
        )
      }
    }
    

    事件处理程序回调函数中的 this

    • JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this的值为 undefined
    class App extends React.Component {
        // 此语法确保 `handleClick` 内的 `this` 已被绑定。
        // 注意: 这是 **实验性** 语法。 使用 Create React App 默认会启用此语法
        handleClick = (e)=> {
            e.preventDefault();
            console.log(e)
        }
      render() {
        return (
            <a href="#" onclick={this.handleClick}>
                Click me
            </a>;
             <a href="#" onclick={this.handleClick。bind(this,id)}> // 向事件处理程序传递参数, 事件对象会被隐式传递
                Click me
            </a>;
        )
      }
    }
    

    Owen 的个人博客

    参考资料
    react 官网
    React 入门教程

  • 相关阅读:
    HTML区块
    HTML表单
    JavaScript 表单验证
    HTML头部
    JavaScript
    设计模式—单例模式的六种写法
    new
    new
    new
    new
  • 原文地址:https://www.cnblogs.com/gaoguowen/p/11279614.html
Copyright © 2020-2023  润新知