[React]核心概念

本文是对React文档:核心概念部分的笔记,内容大致与文档相同。
文档链接
React哲学部分写的很好,务必要看

JSX

JSX是JS的语法扩展,配合react使用,为JS和HTML的混写
JSX支持在大括号({})中书写任何有效JS表达式
同时,JSX也是一个表达式
如下面的例子:

const name="josh perez"
const element=

Hello,{name}

ReactDOM.render( element, document.getElementById("root") );

在属性中嵌入表达式有两种方法:

//使用引号包裹的字面值
const element =
//使用花括号包裹的表达式 const element=

JSX的属性名称使用小驼峰命名,如class->className
React在渲染已经输入的内容前,会默认进行转义,所有的内容在渲染前已经被转换为字符串,可以有效防止XSS
JSX对象:
Babel将会把JSX转译为一个名为React.createElement()的函数调用
即:

const element=(
    

hello

); //与下面的写法相同 const element=React.createElement( "h1", {className:"greet"}, "hello" ); //它事实上创建了一个简化的如下的对象: const element={ type:"h1", props:{ className:"greet", children:"hello" } };

它们也被称为React元素,描述了希望在屏幕上看到的内容,React通过读取这些对象来使用他们构建DOM

元素渲染

元素是构成React应用的最小块
React元素是创建开销很小的普通对象。React DOM更新DOM来与React元素保持一致

React 根 DOM 节点

React 根 DOM 节点大概是类似这样的结构:

这个节点内的所有内容都由React DOM管理。 对于根DOM节点,使用ReactDOM.render()进行渲染
const element=

hello

ReactDOM.render(element,document.getElementById("root"));//root就是根DOM节点的id值

React元素是不可改变对象,一旦被创建就不能修改。更新UI的唯一方法就是创建一个全新的元素。

React 更新策略

React DOM只会进行必要的更新来使DOM达到新的状态。
这种策略只考虑UI在一个指定时间的状态,而不考虑变化的过程,可以减少bug。

组件&props

组件即为代码复用的片段,类似于函数。

组件形式

组件有两种形式:函数和class

//函数组件
function Welcome(props){
    return 

hello,{props.name}

} //ES6-class class Welcome extends React.Component{ render(){ return(
hello,{this.props.name}
); } }

渲染组件

React元素可以是DOM标签、或者是用户自己定义的组件
当用户自定义组件,它会将JSX接收道德属性以及子组件转换为单个对象传递给组件,即props
看如下例子:

class Welcome extends React.Component{
    render(){
  return(
    
hello,{this.props.name}
); } } const elemen= ReactDOM.render( element, document.getElementById('root') );

页面会渲染出:hello,柳刀
注意:自定义标签的名称必须为大写,React将会把小写字母开头的组件视为原生DOM组件。

组件的组合与提取

组件可以进行嵌套组合
通常每个新的React应用程序的顶层组件都应该是App组件
对于一些使用程度叫较高的组件,应该进行额外的定义以使组件更容易维护。

props只读

所有的React组件都必须保护它们的props不被更改

state&生命周期

state与props相似,但是可以完全受控与当前的组件。
组件第一次被渲染到DOM中时,被称为“挂载”(mount)
DOM中组件被删除的时候,应该清除计时器,被称为“卸载”(unmount)
见如下的时钟的例子:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
    //生命周期方法
  //在组件已经被渲染到DOM中后运行
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
//在组件即将被卸载时调用
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
  //计时器函数,通过这个函数更新state
  tick() {
    this.setState({
      date: new Date()
    });
  }
  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } ReactDOM.render( , document.getElementById('root') );

state

state只能在组件初始化时进行赋默认值
state={}
或者在构造函数中进行构造;
state不能在除了以上两个地方之外使用 this.state.xxx 进行更新
只能使用
this.setState({xxx:xxx}) 进行更新

state的更新可能是异步的

React可能会将多个setState()合并为一个调用,而且props,state也可能异步更新,所以不应该依赖它们的值去决定下一个状态。
如:

//wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});
为了解决该问题,可以使用:
// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

使用参数对state进行更新

state的更新会被合并

为了提高性能,React将setState()合并调用,这里的合并是浅合并,简而言之,就和单独调用他们的效果是一样的。

数据是向下流动的

这被称为"自上而下"或者是"单向"数据流,任何的state总是所属于特定的组件,而从state派生的任何数据只能影响树中低于它们的组件。
这类似与一个瀑布,上层组件在瀑布上层,下层组件在瀑布下层,props是水流,state就是各个组件的水源。
React的数据流动是单向的,也是单层的,即数据不能跨组件流动,但是数据流能够跨组件流动。

事件处理

React事件的命名采用小驼峰而不是纯小写
使用JSX时需要传入一个函数作为事件处理函数而不是一个字符串

//传统

//React

React中不能通过返回false阻止默认的行为,必须显示地使用preventDefault(取消默认行为)

//传统html

  Click me

//React
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }
  return (
    
      Click me
    
  );
}

使用class定义组件,事件处理函数通常为一个方法

处理this

class不会默认绑定this,需要手动进行绑定
可以进行:
• 构造函数中绑定this
this.handle=this.handle.bind(this)
• 在调用时绑定this
onClick={this.handle.bind(this)}
• 在声明时使用箭头函数:class fields语法
handle=()=>{}
• 在回调中使用箭头函数
onClick={() => this.handleClick()}
这个方法的问题是在每次渲染组件的时候都会创建不同的回调函数,作为props传入子组件时,这些子组件可能会进行额外的重新渲染,所以不推荐使用。

向事件处理程序传递参数

有两种方法:



在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

条件渲染

元素变量

即通过某个state中的状态进行渲染元素的判断,使用这种方法可以增加代码重用。

&&

例子如下:

Hello!

{unreadMessages.length > 0 &&

You have {unreadMessages.length} unread messages.

}

以上例子能够实现是因为:
在JS中,true&&experssion总是返回experssion
false&&experssion总是返回false

三目运算

这里要注意的是,要在可读性和简便性上做好平衡,保持代码的可读性。
如果代码太复杂,应该提取组件。
阻止组件渲染
不希望渲染某组件时,可以直接render null
以下的例子中将不会进行渲染,用这种方法可以在必要的时候阻止特定组件的渲染。

class ClassName extends React.Component{
    render(){
    return null;
  }
}

组件render返回null并不会影响组件的生命周期。

列表&Key

渲染多个组件

在React中渲染多个组件一般使用map方法,从已有数组中产生新数组。
下面的例子进行了解释:

const numbers=[1,2,3,4,5]
const listItems=numbers.map((number)=>
  • {number}
  • ); ReactDOM.render(
      {listItems}
    , document.getElementById('root') );

    基础列表组件

    规范化,在一个组件中渲染列表:

    class NumberList(props){
        const numbers=props.numbers;
      const listItems = numbers.map((number) =>
        
  • {number}
  • ); return (
      {listItems}
    ) }

    这个例子的作用与前面的例子相同,只不过为每个

  • 分配了一个key

    key

    key的作用是帮助React识别哪些元素发生了改变,所以需要给数组中的每个元素赋予一个特定的值。
    key应该是元素在列表中拥有的独一无二的字符串,通常使用数据中的id作为元素的key。
    如果传入的数据没用id,可以使用元素索引index作为key

    const todoItems = todos.map((todo, index) =>
      // Only do this if items have no stable IDs
      
  • {todo.text}
  • );

    但是如果列表项目的顺序可能发生变化,那不建议使用索引作为key值,这样会使性能变差,还会引起组件状态问题,如果不显示指定key值,React将默认使用索引作为key值。

    用key提取组件

    元素的key应该放在就近数组的上下文中。
    如例子:

    function ListItem(props) {
      // 正确!这里不需要指定 key:
      return 
  • {props.value}
  • ; } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 正确!key 应该在数组的上下文中被指定 ); return (
      {listItems}
    ); }

    在上面的例子中,应该在下文,即ListItem处引入数组key,因为它才是数组中的“直接”元素。
    在大多数情况下,在map()方法中的元素设置key属性总是正确的。

    key只是在兄弟节点之间必须唯一

    数组元素只需要在它所在的元素中唯一即可,不需要全局唯一,在生成两个数组的时候,可以使用相同的key值。
    key只会传递消息给React,而不会传递给所在的组件。需要在组件中使用key值时,应该使用其他的属性名称显式传递值。

    在JSX中嵌入map()

    可以将遍历返回的结果通过map()写在{}中,因为JSX允许在大括号嵌入任何表达式。
    如果一个 map() 嵌套了太多层级,那可能就是你提取组件的一个好时机

    表单

    受控组件

    受控组件即为使React成为state的唯一数据源。渲染表单的React组件还控制着用户输入过程中表单发生的操作。
    如下例:

    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: ''};
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
      handleChange(event) {
        this.setState({value: event.target.value});
      }
      handleSubmit(event) {
        alert('提交的名字: ' + this.state.value);
        event.preventDefault();
      }
      render() {
        return (
          
    ); } }

    在这个例子中,名字表单的值始终为this.state.value,React为state唯一的数据源,handleChange将会在每次案件时执行并更新state。
    对于受控组件,每个state突变都有一个相关的处理函数,这使得修改或者验证用户输入变得更容易。例如可以强制将用户输入大写字母。

    handleChange(event) {
      this.setState({value: event.target.value.toUpperCase()});
    }
    

    textarea标签

    在html中,textarea通过子元素定义文本。
    在react中,使用value属性代替,使得使用