React 基础知识点总结

零、介绍

React.js 是一个帮助你构建页面 UI 的库。若是你熟悉 MVC 概念的话,那么 React 的组件就至关于 MVC 里面的 View。若是你不熟悉也不要紧,你能够简单地理解为,React.js将帮助咱们将界面分红了各个独立的小块,每个块就是组件,这些组件之间能够组合、嵌套,就成了咱们的页面。css

一个组件的显示形态和行为有多是由某些数据决定的。而数据是可能发生改变的,这时候组件的显示形态就会发生相应的改变。而 React.js 也提供了一种很是高效的方式帮助咱们作到了数据和组件显示形态之间的同步。html

React.js 不是一个框架,它只是一个库。它只提供 UI (view)层面的解决方案。在实际的项目当中,它并不能解决咱们全部的问题,须要结合其它的库,例如 ReduxReact-router 等来协助提供完整的解决方法。node

1、Hello World

create-react-app 是来自于 Facebook 出品的零配置命令行工具,可以帮你自动建立基于Webpack+ES6 的最简易的 React 项目模板。react

npm install -g create-react-app 

create-react-app my-app 
cd my-app/ 
npm start 
复制代码

执行完上述命令以后,你能够直接打开 http://localhost:3000,便可以看到你 React APP 的运行效果:git

create-react-app

目录结构:es6

my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js
复制代码

一个简单 Hello World 的例子:shell

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);
复制代码

2、JSX 简介

JSX —— React.js 描述页面 UI 的方式。看起来,JSX 有点像模板语言,其实它是由 React 内部实现的。浏览器中,看到的 JSX 内容转换成 html 显示了出来。npm

一、在 JSX 中使用表达式

index.js :json

import React from 'react'
import ReactDOM from 'react-dom'

const user = {
  firstName: 'React',
  lastName: 'Hello'
}

const formatName = user => `${user.firstName} ${user.lastName}`
const element = <h1>Hello, {formatName(user)}</h1>

ReactDOM.render(element, document.getElementById('root'))
复制代码

推荐在 JSX 代码的外面扩上一个小括号,这样能够防止 分号自动插入bug数组

二、JSX 自己其实也是一种表达式

在编译以后,JSX 其实会被转化为普通的 JavaScript 对象。那就能够在 if 或者 for 语句里使用 JSX,将它赋值给变量,看成参数传入,做为返回值也能够:

index.js

import React from 'react'
import ReactDOM from 'react-dom'

const user = {
    firstName: 'E2E',
    lastName: 'team'
}

const formatName = user => `${user.firstName} ${user.lastName}`

const getGreeting = (user) => {
    if (user) {
        return <h1>Hello, {formatName(user)}!</h1>;
    }
    return <h1>Hello, Stranger.</h1>;
}

const element = <div>{getGreeting(user)}!</div>

ReactDOM.render(element, document.getElementById('root'))
复制代码

三、JSX 嵌套

若是 JSX 标签是闭合式的,那么你须要在结尾处用 />, 就好像 XML/HTML 同样:

const element = <img src={user.avatarUrl} />; 复制代码

JSX 标签一样能够相互嵌套:

const element = (
  <div> <div>头像</div> <h1>Hello, {formatName(user)}!</h1> </div>
)
复制代码

注意:多行的 jsx 要用小括号包裹,里面若是有多个 DOM 节点,也须要用一个 DOM 节点包裹起来,因此这里加了最外面的 div

四、JSX 属性

  • 可使用引号来定义以字符串为值的属性:
const element = <div tabIndex="0"></div>;
复制代码

注意:因为相对HTML 而言,JSX 更加相似于JavaScript, React DOM 使用驼峰命名代替HTML中的属性名。

  • 也可使用大括号来定义以 JavaScript 表达式为值的属性:
const element = <img src={user.avatarUrl}></img>;
复制代码

注意:使用了大括号包裹的 JavaScript 表达式时就不能再到外面套引号了,JSX 会将引号中的内容识别为字符串而不是表达式。

五、JSX可以防注入攻击

render() {
    const content = 'First &middot; <i>Second</i>'
    const element = <div>{content}</div>
    return element
}
复制代码

也就是在执行渲染前,React DOM 会默认将要显示的内容中有任何的标签或者脚本都会进行转义编码,按照字符串直接显示出来。 这能够避免应用被注入,能够避免XSS攻击。因此能够放心地在JSX 当中使用用户输入。

若是须要按照 html 显示的内容,可使用 dangerouslySetInnerHTML 来实现。

const element = <div dangerouslySetInnerHTML={{ __html: content }} /> 复制代码

六、JSX 表明 Objects

Babel 转译器会把 JSX 转换成一个名为 React.createElement() 的方法调用。

下面两种代码的做用是彻底相同的:

const element = (
  <h1 className="greeting"> Hello, world! </h1>
);
复制代码
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);
复制代码

React.createElement() 这个方法首先会进行一些避免bug的检查,以后会返回一个相似下面例子中的对象:

// 注意: 如下示例是简化过的(不表明在 React 源码中是这样)
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};
复制代码

这样的对象被称为 React 元素。它表明全部能够在屏幕上看到的东西。React 经过读取这些对象来构建 DOM 并保持数据内容一致。

3、元素

元素( element )是一个 React 应用的最小组成单元。

一、将元素渲染到DOM中

index.js

import React from 'react'
import ReactDOM from 'react-dom'

const element = <h1>Hello, world</h1>

ReactDOM.render(element, document.getElementById('root'))
复制代码

这里 element 就是一个元素, 元素描述了咱们在屏幕上会看到什么。React 元素不是组件,组件由元素构成。

能够经过 ReactDOM.render 把元素渲染到 DOM 中,idroot 的这个节点在 index.html 中。

浏览器中,能够看到 element 元素显示到了页面上。

二、React只会更新必要的内容

Index.js :

import React from 'react';
import ReactDOM from 'react-dom';

const tick = () => {
  const element = (
    <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div>
  )
  ReactDOM.render(element, document.getElementById('root'))
}

setInterval(tick, 1000)
复制代码

更新必要部分

4、组件&Props

组件( components )可让咱们把 UI 分割成独立的能够复用的片断。概念上来说,组件相似于 JS 的函数,它接收任意的输入(也就是 props ,属性),返回 React 元素。

一、函数式组件

定义一个组件最简单的方式是写一个 JS 的函数:

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

const Welcome = props => {
  return <h1>Hello, {props.name}</h1>
}
const element = <Welcome name="Sara" /> ReactDOM.render(element, document.getElementById('root')) 复制代码

这个函数就是一个完整的 React 组件,由于它接收一个 props 对象做为参数,返回一个 React 元素。这样的组件叫作函数式组件。

二、class 式组件

另一个定义组件的方式就是使用 ES6 的 class:

index.js

import React from 'react'
import ReactDOM from 'react-dom'

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}

const element = <Welcome name="Sara" /> ReactDOM.render(element, document.getElementById('root')) 复制代码

从 React 的角度,上面两个组件是等价的。不过 class 式组件功能会多一些。

三、组件的组合

组件能够在它的输出中引用其它组件,这就可让咱们用同一组件来抽象出任意层次的细节。在React应用中,按钮、表单、对话框、整个屏幕的内容等,这些一般都被表示为组件。

例如,咱们能够建立一个App组件,用来屡次渲染Welcome组件:

index.js

import React from 'react'
import ReactDOM from 'react-dom'

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}

const App = () => {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
复制代码

浏览器中,显示了三个 Welcome

5、State&生命周期

一、class 式组件中才能用 state

class 式组件要比函数式组件功能多,使用 state 就是只能用在 class 式组件中的功能。

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  render() {
    return (
      <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div>
    )
  }
}
const element = <Clock /> ReactDOM.render(element, document.getElementById('root')) 复制代码

Clock 是一个 class 式组件。里面初始化了 state 值。而后 render 函数中,显示出了这个 state 值。

二、给 class 添加生命周期方法

每当 Clock 组件第一次加载到 DOM 中的时候,咱们都想生成定时器,这在 React 中被称为挂载

一样,每当 Clock 生成的这个 DOM 被移除的时候,咱们也会想要清除定时器,这在 React 中被称为卸载

index.js

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

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

  componentDidMount() {
    this.timerID = setInterval(this.tick, 1000)
  }

  componentWillUnmount() {
    clearInterval(this.timerID)
  }

  tick = () => {
    this.setState({
      date: new Date()
    })
  }

  render() {
    return (
      <div> <h1>Hello, World</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div>
    )
  }
}

ReactDOM.render(<Clock />, document.getElementById('root')) 复制代码

组件初次渲染以后,会自动执行 componentDidMount 这个生命周期方法,这里面咱们设置一个定时器,每秒钟执行一下 tick 方法。这里把定时器 id 赋值给了 this.timerID

组件被从 DOM 移除的时候,会自动执行 componentWillUnmount ,这里面咱们须要清除一下定时器,释放资源。

来定义关键的 tick 函数,里面的关键动做就是更新 state 值。注意必定要用 this.setState 来更新。

浏览器中,能够看到每秒钟界面显示时间都会更新。

6、事件处理

一、基本用法

React 元素的事件处理和 DOM元素的很类似。可是有一点语法上的不一样:

  • React事件绑定属性的命名采用驼峰式写法,而不是小写;
  • 若是采用 JSX 的语法,须要传入一个函数做为事件处理函数(推荐使用ES6 的箭头函数),而不是一个字符串(DOM元素的写法)。

index.js :

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

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

    // This binding is necessary to make `this` work in the callback
    // this.handleClick = this.handleClick.bind(this);
  }

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

  render() {
    return (
      <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button>
    )
  }
}

ReactDOM.render(<Toggle />, document.getElementById('root')) 复制代码

注意:类的方法默认是不会绑定 this 的。能够将 handleClick 直接赋值为一个 es6 箭头函数,这样的好处是里面直接使用 this 而无需绑定。因为 this.setState 的异步性,因此参数不能传入对象,而要传入一个函数,才能稳妥的基于以前的状态来得到最新状态值。

二、给事件处理函数传参

一般咱们会为事件处理程序传递额外的参数。例如,如果 id 是你要删除那一行的 id

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

class List extends React.Component {
  deleteRow = id => {
    console.log(id)
  }

  render() {
    return <button onClick={() => this.deleteRow(2)}>Delete Row</button>
  }
}

ReactDOM.render(<List />, document.getElementById('root')) 复制代码

好比有一个列表,这里封装成 List 组件。里面 deleteRow 须要接受行号,这里就是 id ,才能知道要删除哪一行的内容。

若是 deleteRow 中,还想要事件对象:

deleteRow = (id, e) => {
    console.log(id)
  }

  render() {
    return <button onClick={e => this.deleteRow(2, e)}>Delete Row</button>
  }
复制代码

ES6 参数中拿到 e ,把它做为第二个参数传递给 deleteRow 便可。

7、条件渲染

React 中,你能够建立不一样的组件来封装各类你须要的行为。而后还能够根据应用的状态变化只渲染其中的一部分。

React 中的条件渲染和 JavaScript 中的一致,使用 JavaScript 操做符 if条件运算符来建立表示当前状态的元素,而后让 React 根据它们来更新 UI

一、if 条件渲染

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

const UserGreeting = () => <h1>Welcome back!</h1>
const GuestGreeting = () => <h1>Please sign up.</h1>

const Greeting = props => {
  const isLoggedIn = props.isLoggedIn
  if (isLoggedIn) {
    return <UserGreeting />
  }
  return <GuestGreeting />
}

ReactDOM.render(<Greeting isLoggedIn={true} />, document.getElementById('root'))
复制代码

先定义两个函数式组件,一个是跟已经登录的用户打招呼,另外一个跟访客打招呼。下面定义 Greeting 组件。随着 isLoggedIn 的值的不一样,会显示出不一样的内容。

浏览器中,当 isLoggedIn 设置为 truefalse ,会分别显示不一样的打招呼信息。

二、元素变量

你可使用变量来储存元素。它能够帮助你有条件的渲染组件的一部分,而输出的其余部分不会更改。

index.js :

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

const UserGreeting = () => <h1>Welcome Back</h1>
const GuestGreeting = () => <h1>Please Sign Up</h1>
const LoginButton = props => <button onClick={props.onClick}>Login</button>
const LogoutButton = props => <button onClick={props.onClick}>Logout</button>

const Greeting = props => {
    const { isLoggedIn } = props
    if (isLoggedIn) {
        return <UserGreeting />
    }
    return <GuestGreeting />
}

class LoginControl extends Component {
  state = {
    isLoggedIn: false
  }

  handleLoginClick = () => {
    this.setState({
      isLoggedIn: true
    })
  }
  handleLogoutClick = () => {
    this.setState({
      isLoggedIn: false
    })
  }

  render() {
    const { isLoggedIn } = this.state

    let button = null
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />
    }
    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    )
  }
}

ReactDOM.render(<LoginControl />, document.getElementById('root'))
复制代码

添加两个按钮组件进来,一个是登陆,一个是登出。建立一个 LoginControl 组件,初始化 isLoggedIn ,添加登陆和登出对应的处理函数,里面对 isLoggedIn 状态值进行了修改。

三、与运算符 &&

JavaScript 的逻辑与 &&,它能够方便地条件渲染一个元素。

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

const Mailbox = props => {
  const { unreadMessages } = props
  return (
    <div> <h1>Hello!</h1> {unreadMessages.length > 0 && ( <h2>You have {unreadMessages.length} unread messages.</h2> )} </div>
  )
}

const messages = ['React', 'Re: React', 'Re:Re: React']
ReactDOM.render(
  <Mailbox unreadMessages={messages} />, document.getElementById('root') ) 复制代码

定义 Mailbox 组件,属性中拿到未读邮件的数组,下面用 && 号实现 if 的效果,若是未读邮件数量大于 0,就显示未读邮件的数量;若是数量为 0,那么大括号里面内容就求值为 undefined ,也就是什么都不会显示了。

四、三目运算符

在下面的例子中,咱们用它来有条件的渲染一小段文本:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div>
  );
}
复制代码

一样它也能够用在较大的表达式中:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}
复制代码

五、阻止组件渲染

在极少数状况下,你可能但愿隐藏组件,即便它被其余组件渲染。让 render 方法返回 null 而不是它的渲染结果便可实现。

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

const WarningBanner = (props) => {
    if (!props.warn) {
        return null;
    }

    return (
        <div className="warning"> Warning! </div>
    );
}

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

    handleToggleClick = () => {
        this.setState({
            showWarning: !this.state.showWarning
        });
    }

    render() {
        return (
            <div> <WarningBanner warn={this.state.showWarning} /> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') ); 复制代码

8、列表 & Keys

一、渲染多个组件

先看下在 Javascript 中如何转化列表:

咱们使用 map()函数让数组中的每一项翻倍,咱们获得了一个新的数列doubled

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
复制代码

React中,把数组转化为数列元素的过程是类似的:

index.js :

import React from 'react'
import ReactDOM from 'react-dom'

const messages = ['hello', 'hi', 'how are you']

const List = props => {
  const { messages } = props

  const list = messages.map(t => <li>{t}</li>)
  return <ul>{list}</ul>
}

ReactDOM.render(<List messages={messages} />, document.getElementById('root'))
复制代码

注:此时打开浏览器控制台会有报错信息:Warning: Each child in an array or iterator should have a unique "key" prop. 。缘由是每个列表条目都应该有一个独一无二的 key

二、Key

把数据的 id 做为 key 是很是常见的作法:

index.js

import React from 'react'
import ReactDOM from 'react-dom'

const messages = [
  {
    id: 1,
    text: 'React'
  },
  {
    id: 2,
    text: 'Re: React'
  },
  {
    id: 3,
    text: 'Re:Re: React'
  }
]

const List = props => {
  const { messages } = props
  const list = messages.map(t => <li key={t.id}>{t.text}</li>)
                            
  return <ul>{list}</ul>
}

ReactDOM.render(<List messages={messages} />, document.getElementById('root'))
复制代码

一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。一般,咱们使用来自数据的 id 做为元素的key。

实际开发中的数据通常都是配有 id 的,将 id 做为 key 是一个很好的作法。若是用数组 index 做为 key 也是勉强能够的,可是因为 index 可能会随着数组元素的增减发生变化,若是列表能够从新排序,这会致使渲染变得很慢。

9、表单

当用户提交表单时,HTML 的默认行为会使这个表单跳转到一个新页面。在 React 中亦是如此。

但大多数状况下,咱们都会构造一个处理提交表单并可访问用户输入表单数据的函数。实现这一点的标准方法是使用一种称为受控组件的技术。

一、受控组件

<input><select> 都要绑定一个 change 事件,每当表单的状态发生变化,都会被写入组件的 state 中,这种组件在 React 中被称为受控组件。

二、form 基本用法

index.js :

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class Form extends Component {
  constructor(props) {
    super(props);
    this.state = { username: '' }
  }

  handleChange = e => {
    this.setState({
      username: e.target.value
    })
  }

  handleSubmit = e => {
    console.log(this.state.username)
    e.preventDefault()
  }

  render() {
    return (
      <div> username: <input type="text" value={this.state.username} onChange={this.handleChange} /> <button onClick={this.handleSubmit}>提交</button> </div> ) } } ReactDOM.render(<Form />, document.getElementById('root')) 复制代码

因为 value 属性是在咱们的表单元素上设置的,所以显示的值将始终为 React 数据源上this.state.value 的值。因为每次按键都会触发 handleChange 来更新当前 React 中的 state,所展现的值也会随着不一样用户的输入而更新。

三、处理多个输入

你有处理多个受控的 input 元素时,你能够经过给每一个元素添加一个 name 属性,来让处理函数根据 event.target.name 的值来选择作什么。

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class Form extends Component {
  constructor(props) {
    super(props);
    this.state = { username: '', email: '' }
  }

  handleChange = event => {
    const { value, name } = event.target

    this.setState({
      [name]: value
    })
  }

  handleSubmit = e => {
    console.log(`${this.state.username} ${this.state.email}`)
    e.preventDefault()
  }

  render() {
    return (
      <div>
        Username:
        <input
          name="username"
          type="text"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <br />
        Email:
        <input
          name="email"
          type="text"
          value={this.state.email}
          onChange={this.handleChange}
        />
        <br />
        <button onClick={this.handleSubmit}>提交</button>
      </div>
    )
  }
}

ReactDOM.render(<Form />, document.getElementById('root'))
复制代码
相关文章
相关标签/搜索