React笔记

React笔记

以下同样同样同样是我在复习学习React时,整理的笔记,可以参考学习

React官网:React 官方中文文档 – 用于构建用户界面的 JavaScript 库 (docschina.org)

这上面的教程就很不错,以下的主要内容都参考自这个官方教学文档

(示例代码放的比较多,目的是通过代码理解抽象的概念,另一方面同时也能积累一些代码经验)

文章目录

      • React笔记
        • 1.什么是React
        • 2.引入React
          • 2.1React基本使用
          • 2.1React脚手架的使用(推荐用这种方式)
        • 3.JSX
          • 3.1简单介绍
          • 3.2JSX相关语法及特性
        • 4.元素渲染
          • 4.1将一个元素渲染为DOM
          • 4.2更新已渲染的元素
        • 5.组件&Props
          • 5.1函数组件
          • 5.2类组件
          • 5.3渲染组件
          • 5.4渲染过程
          • 5.5组件组合
          • 5.6提取组件
          • 5.7Props是只读的
        • 6.State&生命周期
          • 6.1State
          • 6.2生命周期
          • 6.3数据是向下流动的
        • 7.事件处理
          • 7.1事件处理
          • 7.2事件处理函数声明位置
          • 7.3向事件处理函数传递参数
        • 8.条件渲染
          • 8.1可以用if进行条件渲染
          • 8.2设置元素变量
          • 8.3更方便的语法
          • 8.4阻止组件渲染
        • 9.列表&Key
          • 9.1列表
          • 9.2key
        • 10.表单
          • 10.1受控组件
          • 10.2处理多个输入
          • 11.状态提升
          • 12.组合vs继承
          • 12.1组合关系
          • 12.2特例关系
          • 12.3继承
          • 13.React的设计理念
        • 14.补充
        • 15.总结

1.什么是React

在构建前端页面时,仅仅使用三剑客html、css、js的话,理论上可以写出各种页面,但是写起来非常复杂,比如js控制DOM时用原生的写法getElementById写起来很繁琐,可读性也不高,因此可以使用一些框架,来简化开发过程,我了解的主流框架有Vue、React,都可以简化开发过程且提升渲染效率。以下是关于React具体的相关知识。

React是一个用于构建用户界面的JavaScript库

React笔记_第1张图片

React具有以上三个特点,且前端人员可以通过React Native来进行原生移动应用开发

声明式:只需要描述UI看起来是什么样子

组件化:把页面中的构成组件化

一次学习,随处编写:开发VR、开发web、开发移动端等等

但是学习React要额外学习一门JSX语言,它是一个JavaScript的语法扩展

2.引入React

2.1React基本使用

安装React

npm i react react-dom

引入react和react-dom的两个js文件

<script src="./node_modules/react/umd/react.development.js">script>
<script src="./node_modules/react-dom/umd/react-dom.development.js">script>

如果要写JSX代码,还需要另外引入

2.1React脚手架的使用(推荐用这种方式)

初始化项目,命令: npx create-react-app my-app

启动项目(在项目的根目录执行),在项目根目录执行命令: npm start

(要注意版本问题)

3.JSX

3.1简单介绍
const element = 

Hello, world!

;

这种语法就是JSX语法

JSX 可以生成 React “元素”,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。React 不强制要求使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还可以使 React 显示更多有用的错误和警告消息。

3.2JSX相关语法及特性

在JSX中嵌入表达式

在 JSX 语法中,你可以在大括号内放置任何有效的JavaScript表达式

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  

Hello, {formatName(user)}!

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

同时JSX也是一个表达式

也就是说,你可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX:

function getGreeting(user) {
  if (user) {
    return 

Hello, {formatName(user)}!

; } return

Hello, Stranger.

; }

JSX可以使用大括号来在属性值中插入JavaScript表达式,比如:

const element = ;

这种方式等于把属性也改成了变量,根据其具体的值而更新渲染

JSX能包含很多子元素

const element = (
  

Hello!

Good to see you here.

);

相当于在JSX里面就能写html语句

4.元素渲染

元素是构成React应用的最小砖块

可以理解每一个const就是一个元素,描述了想在屏幕上看到的内容

4.1将一个元素渲染为DOM

需要将元素传入ReactDOM.render();

const element = 

Hello, world

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

在html中要有一个根DOM节点

<div id="root">div>
4.2更新已渲染的元素

React元素是不可变的,如果想要更新就要创建全新的元素,将其渲染进ReactDOM.render()里面

比如一个计时器的例子:

function tick() {
  const element = (
    

Hello, world!

It is {new Date().toLocaleTimeString()}.

); ReactDOM.render(element, document.getElementById('root')); } setInterval(tick, 1000);

每隔一秒调用tick函数,更新了element,然后调用ReactDOM.render(),重新渲染

实现了动态更新

React DOM会将元素及其子元素与之前的状态进行比较,而只更新变化的部分来达到预期

这样就提高了渲染效率!!

5.组件&Props

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

组件允许将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。

这种思想将一整个UI拆分成一个一个可复用的小块,非常高效

组件名称必须以大写字母开头!!!

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

Hello, {props.name}

; }

这是一个简单的函数组件,接收props参数,返回react元素

5.2类组件
class Welcome extends React.Component {
  render() {
    return 

Hello, {this.props.name}

; } }

这两个组件的效果是一样的

5.3渲染组件

React元素可以是DOM标签、也可以是用户自定义的组件

const element = 
; const element = ;

当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。

5.4渲染过程

React笔记_第2张图片
这个例子可以帮助我们理解React的渲染过程

5.5组件组合

在构建页面时,我们往往要把页面拆分成许多个组件,这些组件之间也存在一定联系,可以进行嵌套、组合

function Welcome(props) {
  return 

Hello, {props.name}

; } function App() { return (
); } ReactDOM.render( , document.getElementById('root') );

这是一个多次渲染welcome组件的app组件

5.6提取组件

就是将组件拆分成更小的组件

有时候一个组件有很多的嵌套关系,难以维护,难以理解,而且难以复用

这时就可以将某些功能拆分出来,设计成独立的组件

比如这是一个组件实现的功能:

function Comment(props) {
  return (
    
{props.author.name}
{props.author.name}
{props.text}
{formatDate(props.date)}
); }

这是拆分后的完整代码:

function formatDate(date) {
  return date.toLocaleDateString();
}    //一个获取时间的函数

function Avatar(props) {
  return (
    {props.user.name}
  );
}      //Avatar组件,渲染图片

function UserInfo(props) {
  return (
    
{props.user.name}
); } //UserInfo组件,渲染用户信息 function Comment(props) { return (
{props.text}
{formatDate(props.date)}
); } //Comment组件,渲染整体架构 const comment = { date: new Date(), text: 'I hope you enjoy learning React!', author: { name: 'Hello Kitty', avatarUrl: 'https://placekitten.com/g/64/64', }, }; //jsx元素,用来储存信息 ReactDOM.render( , document.getElementById('root') ); //进行渲染

在多个组件嵌套时,我个人觉得属性很难有一个清晰的思路

把每个组件有的属性画出来会比较好一些,或用注释标注在代码旁
React笔记_第3张图片

把要用到的属性提前整理出来,在传递、调用的时候就不容易搞错

整体的思路也会更清晰一些

5.7Props是只读的

我们要尽量避免修改Props,如果要进行修改变化,要用到下面的State

6.State&生命周期

6.1State

在前面时钟的例子中,需要多次更新某一值,因此解决办法是多次调用ReactDOM.render()方法,但是还有一种更简单的方法,用类组件中的State

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }       

  render() {
    return (
      

Hello, world!

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

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

当State值变化时,会自动重新渲染变化的部分

注意

1.不能直接修改State的值,而是应该调用setState方法进行修改

2.State和Props的更新可能是异步的

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

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

3.setState更新会被合并,单独更新,系统自动合并

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }
6.2生命周期

组件由第一次渲染到最后被销毁的生命周期

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),      //调用自身的方法要加this
      1000
    );
  }           //在组件第一次被渲染后执行

  componentWillUnmount() {
    clearInterval(this.timerID);
  }            //在组件被销毁时执行

  tick() {
    this.setState({
      date: new Date()
    });
  }          //修改State值

  render() {
    return (
      

Hello, world!

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

); } } ReactDOM.render( , document.getElementById('root') );
6.3数据是向下流动的

State具有较好的封装性,外部无法直接访问,但是可以在组件内作为参数传递给子组件,且只能由父组件传给子组件

比如:


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

7.事件处理

7.1事件处理

html与jsx在事件处理上有一些差别

//在html中的事件处理


//在jsx中的事件处理

在 React 中另一个不同点是不能通过返回 false 的方式阻止默认行为。必须显式的使用 preventDefault

//html

  Click me



//jsx
function ActionLink() {
  function handleClick(e) {     //e是一个合成事件
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    
      Click me
    
  );
}
7.2事件处理函数声明位置

当定义一个类组件的时候,通常的做法是将事件处理函数声明为 class 中的方法。例如,下面的 Toggle 组件会渲染一个让用户切换开关状态的按钮:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

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

  handleClick() {       //事件处理函数,声明为class中的方法
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      
    );
  }
}

ReactDOM.render(
  ,
  document.getElementById('root')
);
7.3向事件处理函数传递参数

这两种方式是一样的



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

8.条件渲染

在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。

8.1可以用if进行条件渲染
//两个不同的方法
function UserGreeting(props) {
  return 

Welcome back!

; } function GuestGreeting(props) { return

Please sign up.

; } //函数组件 function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return ; } return ; } ReactDOM.render( // Try changing to isLoggedIn={true}: , document.getElementById('root') );

使用三目运算符也可以,而且更方便一些

8.2设置元素变量
class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }

  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }

  handleLogoutClick() {
    this.setState({isLoggedIn: false});
  }

  render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button;
    if (isLoggedIn) {
      button = ;
    } else {
      button = ;
    }

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

用button储存最后要渲染哪一个,用State中的值进行判断

8.3更方便的语法

&&运算符

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 会忽略并跳过它。

三目运算符

//简单的用法
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    
The user is {isLoggedIn ? 'currently' : 'not'} logged in.
); } //复杂一点的 render() { const isLoggedIn = this.state.isLoggedIn; return (
{isLoggedIn ? : }
); }
8.4阻止组件渲染

在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。

function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    
Warning!
); } class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true}; this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(state => ({ showWarning: !state.showWarning })); } render() { return (
); } } ReactDOM.render( , document.getElementById('root') );

9.列表&Key

9.1列表

用map进行遍历,渲染一组组件

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

    map后面还能传入更多参数,比如下标等等

    用map可以很方便地把数目未定,但渲染方式相同的组件一个一个渲染出来

    9.2key

    key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。

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

    当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 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') );

    10.表单

    使用 JavaScript 函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”1

    10.1受控组件
    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 (
          
    ); } }

    value属性的值由state里的值实时控制

    而在 React 中,