IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React 核心概念(2) -> 正文阅读

[JavaScript知识库]React 核心概念(2)

React 核心概念(2)

?

1.组件介绍及 props

?

1.1 组件分类

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

?
组件分为 class 组件函数组件 两种,下面给出两种组件的简单示例:

// 函数组件
function FunctionComponent(name, address) {
  return `${name}${address}读书!`;
}

// class 组件
class ClassComponent extends React.Component {
  render() {
    return <h1>张三在北京读书!</h1>;
  }
}

?

1.2 组件渲染

function FunctionComponent(props) {
  const { name, address } = props;
  return `${name}${address}读书!`;
}

const element = <FunctionComponent name="张三" address="北京" />

ReactDOM.render(
  element,
  document.getElementById('root') // <div id="root" />
);

?
下面来简单分析一下执行过程:

?
首先,element 是 JSX 语法,根据上一节内容知道,最后 element 应该是这样的:

const element = {
  type: 'div',
  props: {
    id: 'root',
    children: {
      type: 'h1',
      props: {
        name: '张三',
        address: "北京",
        children: '张三在北京读书1',
      },
    }
  }
};

?
由上面的element的第6行可以看到,虽然我们是定义了一个组件,实际上经过 BabelReact.createElement() 的转换之后,拿到的是用于描述页面展示内容的 React 元素

?
其次,将此 element 传入 ReactDOM.render() 中作为第一个参数,并传入需要被挂载的 DOM 节点作为第二个参数,即可将该 element 元素渲染成一个 DOM 节点。

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

?

1.3 组合组件

?
组件可以在其输出中引用其他组件。

// Parent,Child 都是组件
function Parent() {
  return (
    <div>
      <Child />
    </div>
  );
}

?

1.4 提取组件

?
将组件拆分为更小的组件。

?
什么时候需要考虑拆分组件?

?
如果 UI 中有一部分被多次使用(Button,Panel,Avatar),或者组件本身就足够复杂(App,FeedStory,Comment),那么它就是一个可复用组件的候选项。

?

1.5 Props 的只读性

?
无论是使用函数组件还是 class 组件,都决不能修改自身的 props。这是规则,必须要遵守!

?

2.State

?
在 hooks 出现之前,只有 class 组件才有 state 功能,针对 hooks (函数组件专用),后面会专门的章节,现在主要使用 class 组件的 this.setState()this.state 来说明:

import React from 'react';

class ClassComp extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      count: 0,
    }
  }
  increase = () => {
    const { count } = this.state;
    this.setState({ count: count + 1 });
  }

  render () {
    const { count } = this.state;
    return (
      <>
        <div>{count}</div>
        <button onClick={this.increase}>点击</button>
      </>
    )
  }
} 

export default ClassComp;

?
上面代码中,就是一个简单的增加计数的例子。

?

2.1 不要直接修改 State

?

this.state.count = 100;

这样的修改方式是错误的,react 是通过 Object.is() 来 进行新旧 state 的比较,这样直接去修改,会导致 Object.is() 判断新旧 state 中的 count 相等,从而导致组件不会被重新渲染

?

2.2 State 的更新可能是异步的

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

increase = () => {
  const { count } = this.state;
  this.setState({ count: count + 1 });
  this.setState({ count: count + 1 });
  this.setState({ count: count + 1 });
}

?
这样点击一次之后,count 是变成 3 吗?

?
当然我这样问了,很多同学就会发现这是一个坑,没错,正确答案是:1 。

?
明明调用了三次 this.setState() ,为什么会这样呢,这就回到了本小节一开始说到的:出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

?
虽然你点击之后,调用了三次 setState(),但是实际上由于合并调用,这样连续三次调用时的 state 其实都是0,因此每次增加都是 0 + 1,自然最后结果为 1.

?
难道就不能依赖他们的值来更新下一个状态吗?

?
那么如果在某些情况下,我必须要依赖上一个 state 怎么办?这也是有解决办法的,考虑下面代码:

increase = () => {
  const { count } = this.state;
  this.setState(state => ({ // 此时 state.count = 0
    count: state.count + 1,
  }));
  this.setState(state => ({ // 此时 state.count = 1
    count: state.count + 1,
  }));
  this.setState(state => ({ // 此时 state.count = 2
    count: state.count + 1,
  }));
}

?
这时候会发现,每次点击增加按钮,值增加 3。这是为什么呢?

?
这是因为,我们在调用 this.setState() 的时候,不再传入一个对象,而是传入一个函数,这个函数的参数保证是最新的state,这样在每次增加时,都是是用最新的 state 里面的值来做加数。

?
注1:this.setState() 传入函数的参数实际有两个,第一个参数是最新的 state,第二个参数最新的 props

?
注2:其他可及时获取state更改后的值的方式(同步)

?
1.通过在setState中传入第二个参数(函数),setState 是同步

this.setState({
  count: this.state.count + 1,
}, () => {
  console.log(this.state.count);	// 同步
});

?
2. setTimeout 中 setState 是同步

setTimeout(() => {
  this.setState({
    count: this.state.count + 1,
  });
  console.log(this.state.count);	// 同步
}, 0);

?
3.自己定义的 DOM 事件, setState 是同步

componentDidMount = () => {
  document.body.addEventListener('click', this.handleClick);
}

// 组件销毁时执行
componentWillUnmount = () => {
  // 即是销毁自定义的DOM事件
  document.body.removeEventListener('click', this.handleClick);
}

handleClick = () => {
  this.setState({
    count: this.statecount + 1,
  });
  console.log(this.state.count);
}

?
在这里记得要销毁自定义的 DOM 事件,包括上面使用 setTimeout ,也需要在 componentWillUnmount 中执行 clearTimeout。

?

2.3 State 的更新会被合并

?
当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。(当传入的是函数时,则不会合并,如2.2中例子)

?
考虑下面代码:

// 省略部分代码
constructor (props) {
  super(props);
  this.state = {
    count: 0,
    total: 100,
  }
}

increase = () => {
  const { count } = this.state;
  this.setState({
    count: count + 1,
  });
}

this.state 中的 total 值为100,在 increase 函数中,我们增加 count 的值,这时候 React 会完整保留this.state.total,而完全替换了 this.state.count

?
注:这里的合并是浅合并,因此即使 this.state 中的值是引用类型,在替换时实际上是替换了地址。

?

3.数据流是向下移动的

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

?
不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。

?
组件可以选择把它的 state 作为 props 向下传递到它的子组件中:

<h1>{this.state.count}</h1>

?
对于自定义组件同样适用:

<selfComp count={this.state.count} />

?
每个组件都是真正独立的(引自: React 数据是向下流动的

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:15:56  更:2021-09-02 11:16:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 15:50:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码