React学习笔记

元素渲染

如何更新已渲染的元素

React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。一个元素就像电影的单帧:它代表了某个特定时刻的 UI。

更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()

React 只更新它需要更新的部分

React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态

组件

从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素

注意: 组件名称必须以大写字母开头。

React 会将以小写字母开头的组件视为原生 DOM 标签。例如,

代表 HTML 的 div 标签,而 则代表一个组件,并且需在作用域内使用 Welcome。

建议从组件自身的角度命名 props,而不是依赖于调用组件的上下文命名。

1,函数组件

function Welcome(props) {
  return 

Hello, {props.name}

; }

此函数为有效的React组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素

2,ES6 的 class 来定义组件

class Welcome extends React.Component {
  render() {
    return 

Hello, {this.props.name}

; } }

Props的只读性

组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。

所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

State & 生命周期

State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。

正确地使用 State

1,不要直接修改 State

// 错误
this.state.comment = 'Hello';

// 正确
this.setState({comment: 'Hello'});

2,State 的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。

// 错误
this.setState({
  counter: this.state.counter + this.props.increment,
});

// 正确  
可以让 setState() 接收一个函数而不是一个对象。
这个函数用上一个 state 作为第一个参数,
将此次更新被应用时的 props 做为第二个参数

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


State 的更新会被合并

当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。

state合并为浅合并,只替换更新的对象,不会影响到其他对象

数据是向下流动的

state 属于“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。

如果你把一个以组件构成的树想象成一个 props 的数据瀑布的话,那么每一个组件的 state 就像是在任意一点上给瀑布增加额外的水源,但是它只能向下流动。

生命周期

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
    
  // 方法会在组件已经被渲染到 DOM 中后运行    
  componentDidMount() {

  }
  
  // 方法会在组件被卸载时运行
  componentWillUnmount() {

  }

  render() {
    return (
      

Hello, world!

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

); } }

事件处理

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串
// 传统的html


// 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 Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 为了在回调中使用 `this`,这个绑定是必不可少的
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      
    );
  }
}

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


注:想要在回调中使用this,那么必须要在在构造方法中绑定进行绑定
回调函数中的 this,
在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined

也可以使用箭头函数进行this的指向

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 此语法确保 `handleClick` 内的 `this` 已被绑定。
    return (
      
    );
  }
}


此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。

与运算符 &&

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    

Hello!

{unreadMessages.length > 0 &&

You have {unreadMessages.length} unread messages.

}
); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( , document.getElementById('root') );

在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。
因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它

列表 & Key

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  
  • {todo.text}
  • ); 使用map进行遍历,可加下标

    注:在 map() 方法中的元素需要设置 key 属性。
    如果列表项目的顺序可能会变化,不建议使用索引用作key值,这个会导致性能变差,还可能引发状态的问题,可以使用如下方式:

    function ListItem(props) {
      // 正确!这里不需要指定 key:
      return 
  • {props.value}
  • ; } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 正确!key 应该在数组的上下文中被指定 ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('root') );

    表单

    受控组件:表单元素(如、 和 )之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新

    受控组件个人理解,就是把state的值绑定到表单元素的value上,当触发事件时,重新进行setState的值,使 React 的 state 成为“唯一数据源”

    处理多个输入

    当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。

    class Reservation extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          isGoing: true,
          numberOfGuests: 2
        };
    
        this.handleInputChange = this.handleInputChange.bind(this);
      }
    
      handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        // 此处为表单元素的name
        const name = target.name;
        
        
        // 使用 ES6 计算属性名称的语法更新给定输入名称对应的 state 值
        this.setState({
          [name]: value
        });
      }
    
      render() {
        return (
          

    ); } }

    成熟的解决方案

    如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 (Formik 是不错的选择。然而,它也是建立在受控组件和管理 state 的基础之上 —— 所以不要忽视学习它们。

    状态提升

    所谓状态提示其实就是把多个组件需要共享的state向上移动到它们的最近公共父组件。

    简而言之,就是在父组件当中创建一个方法,子组件数据改变后,通过调用父组件的方法,把值传递到父组件,修改父组件state中的值

    // 子组件
    class TepmperatureInput extends React.Component {
        constructor(props) {
            this.handleChange = this.handleChange.bind(this);
        }
        
        handleChange(e){
            // 父组件定义的方法,把值传递给父组件
            this.props.onTemperatureChange(e.target.value);
        }
        
        render() {
            const temperature = this.props.temperature
            return (
                
    ) } } // 父组件 class ParentComponent extends React.Component { constructor(props) { super(props); this.state = {inputVal:''} } // 子组件回调时方法 onTemperatureChange(temperature) { this.setState({inputVal:temperature}) } render() { const inputVal = this.state.inputVal return (
    ) } } tips:建议如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。

    组合 vs 继承

    function SplitPane(props) {
      return (
        
    // 子组件中直接使用props方式调用即可 {props.left}
    {props.right}
    ); } // left right 名字可以自己定义,直接当成props传入即可 function App() { return ( } right={ } /> ); }

    React元素本质就是对象,所以你可以把它们当作 props,像其他数据一样传递。这种方法可能使你想起别的库中“槽”(slot)的概念,但在 React 中没有“槽”这一概念的限制,你可以将任何东西作为 props 进行传递。

    React 哲学

    确定了组件层级,可以编写对应的应用了。最容易的方式,是先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。

    构建应用方式

    • 自上而下 意味着首先编写层级较高的组件 , 应用比较简单时使用
    • 自下而上 意味着从最基本的组件开始编写 应用比较复杂时使用

    确定 state 放置的位置

    注意:

    React 中的数据流是单向的,并顺着组件层级从上往下传递。

    哪个组件应该拥有某个 state 这件事,对初学者来说往往是最难理解的部分。尽管这可能在一开始不是那么清晰,但你可以尝试通过以下步骤来判断:

    • 找到根据这个 state 进行渲染的所有组件。
    • 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
    • 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
    • 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。

    你可能感兴趣的:(学习笔记)