复习一下react基础只是,写vue写多了,基本的语法都忘记了,现在react 16之后有一些变更,例如很多周期函数后面就不用了,也引入react hook使得函数组件可以使用state和一些类似组件的特性,所以还是更新一下,方便后续技术栈切换有细节的遗漏…
react基础
1) ReactDOM.render
react.js 是 React 的核心库
react-dom.js 是提供与 DOM 相关的功能,用于将模板转为 HTML 语言,并插入指定的 DOM 节点
将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()
1
2
3
4 ReactDOM.render(
<h1>Hello, world!</h1>, // react元素
document.getElementById('example') // dom节点
);
2) jsx
它被称为 JSX,是一个 JavaScript 的语法扩展
遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析
给JSX元素加class要用className代替
1 | ReactDOM.render(<h1 className="bg">hello world</h1>, document.getElementById('root')) |
标签的for属性要使用htmlFor代替
1 | let ele = ( |
style必须是一个对象的形式
1 | ReactDOM.render(<h1 style={{background: 'red'}}>hello world</h1>, document.getElementById('root')) |
JSX 标签的第一部分指定了 React 元素的类型。大写字母开头的 JSX 标签意味着它们是 React 组件。这些标签会被编译为对命名变量的直接引用
1 | import React from 'react'; // 必须引入react |
使用引号,来将属性值指定为字符串字面量,使用大括号,来在属性值中插入一个 JavaScript 表达式,在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号
1 <div age={age} gender="man">{username}</div>
Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。
1 | const element = ( |
一个标签里面没有内容,你可以使用 /> 来闭合标签
1 | const element = <img src={user.avatarUrl} />; |
在 JSX 类型中使用点语法
1 | import React from 'react'; |
动态组件
vue中通过components的is属性动态加载组件,react中要定义一个大写字母开头的变量,在jsx会解析成react元素
1 | import React from 'react'; |
JSX 防止注入攻击
1 | const title = response.potentiallyMaliciousInput; // 用户的潜在危险输入 |
React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
当props插入HTML需要
dangerouslySetInnerHTML使用转译
1 | let str = '<div>如果我是可能带有xss攻击的代码</div>' |
多行的时候建议将内容包裹在括号中,虽然这样做不是强制要求的,但是这可以避免遇到自动插入分号陷阱
1 | let userInfo = ( |
HTML 语言直接写在 JavaScript 语言之中,不加任何引号,它允许 HTML 与 JavaScript 的混写
1 | let names = ['lucy','lily'] |
JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员
1 | var arr = [ // 将react元素存入变量 |
React 独有的 JSX 语法,跟 JavaScript 不兼容。凡是在script中单独使用 JSX 的地方,都要加上 type=”text/babel”
3) 组件 - 函数组件和class组件
组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思
组件的返回值只能有一个根元素,一般可以是用React.fragment进行包裹,有点类似vue中template将函数组件转换成 class 组件
1 | class Comp extends React.Component { // 1.创建一个同名的 ES6 class,并且继承于 React.Component |
组件名称必须以大写字母开头。
会将以小写字母开头的组件视为原生 DOM 标签
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素
- 函数组件与 class 组件
函数组件
函数组件接收一个单一的 props 对象并返回了一个React元素
定义组件最简单的方式就是编写 JavaScript 函数,
函数组件本质上就是 JavaScript 函数。
1 | // 函数里面是没有this |
类组件
你同时还可以使用 ES6 的 class 来定义组件:
1 | export default class Welcome extends React.Component { // 定义 class 组件,需要继承 React.Component |
组件总结
1 | - 无论是使用函数或是类来声明一个组件,它决不能修改它自己的 props。 |
4) 组件的生命周期
- 挂载
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
1 | constructor() |
- 更新
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
1 | static getDerivedStateFromProps() |
- 卸载
当组件从 DOM 中移除时会调用如下方法:
1 | componentWillUnmount() |
- 错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
1 | static getDerivedStateFromError() |
5) Props
- 当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)转换为单个对象传递给组件,这个对象被称之为 “props”
1 | function Welcome(props) { |
- Props 默认值为 “True
如果你没给 prop 赋值,它的默认值是 true
1 | <MyTextBox autocomplete /> // 等价于 |
- 属性展开
如果你已经有了一个 props 对象,你可以使用展开运算符 … 来在 JSX 中传递整个 props 对象
1 | <Greeting firstName="Ben" lastName="Hector" />; |
你还可以选择只保留当前组件需要接收的 props,并使用展开运算符将其他 props 传递下去。
1 | const Button = props => { |
- Props 的只读性
组件无论是使用函数声明还是通过 class 声明,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
- this.props.children
包含在开始和结束标签之间的 JSX 表达式内容将作为特定属性 props.children 传递给外层组件
1 | class RootContent extends React.Component{ |
- props类型校验
1 | import PropTypes from 'prop-types'; |
- 限制单个元素
PropTypes.element 来确保传递给组件的 children 中只包含一个元素
1 | mport PropTypes from 'prop-types'; |
- 默认prop值
可以通过配置特定的 defaultProps 属性来定义 props 的默认值:
1 | class Greeting extends React.Component { |
5) state
- state 包含了随时可能发生变化的数据。state 由用户自定义,它是一个普通 JavaScript 对象,并且完全受控于当前组件
class 构造函数,然后在该函数中为 this.state 赋初值
1 | constructor(props) { |
- 请把 this.state 看作是不可变的,永远不要直接改变 this.state,因为后续调用的 setState() 可能会替换掉你的改变
不要直接修改 State,构造函数是唯一可以给 this.state 赋值的地方:
1 | // Wrong |
使用setState 修改 State。setState() 的第一个参数除了接受函数外,还可以接受对象类型
1 | this.setState({ //异步更新,并且会合并多次状态变化一次更新 |
同一周期内会对多个 setState 进行批处理,会将传入的对象浅层合并到新的 state 中
1 | Object.assign( |
setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
除非shouldComponentUpdate() 返回 false,否则 setState() 将始终执行重新渲染操作
将 setState() 不是立即更新组件,它会
批量推迟更新。componentDidUpdate 或者 setState 的回调函数(setState(updater, callback))可以保证在应用更新后触发.
setState 通过触发一次组件的更新来引发重绘
1 | 重绘指的就是引起 React 的更新生命周期函数4个函数: |
在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用 setState 不会同步更新 this.state,除此之外的setState调用会同步执行this.state。
所谓“除此之外”,指的是绕过React通过 addEventListener 直接添加的事件处理函数,还有通过setTimeout || setInterval 产生的异步调用
简单一点说, 就是经过React 处理的事件是不会同步更新 this.state的. 通过 addEventListener || setTimeout/setInterval 的方式处理的则会同步更新。
1 | setTimeout(function(){ |
6) 事件处理
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
1 | // 1. React 事件的命名采用小驼峰式(camelCase),而不是纯小写。 |
对事件this的绑定
1 | // 1.在构造函数中绑定this(推荐这样写) |
向事件处理程序传递参数
1 | // 1.箭头函数,需要显示的传递e事件对象 |
6) 条件渲染
你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。
React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
元素变量 - 你可以使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分
1 | render() { |
- 与运算符 &&
在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。
1 | return ( |
- 或运算符 ||
- 三目运算符 condition ? true : false
1 | render() { |
- 阻止组件渲染
极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染
1 | function WarningBanner(props) { |
在组件的 render 方法中返回 null 并不会影响组件的生命周期
- 使用 Javascript 中的 map() 方法渲染多个组件
1 | const numbers = [1, 2, 3, 4, 5]; |
- key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串
元素的 key 只有放在就近的数组上下文中才有意义
1 | function ListItem(props) { |
key 只是在兄弟节点之间必须唯一
数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值
key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值
1 | const content = posts.map((post) => |
7) 表单
受控组件
使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做
“受控组件”。input/textarea/select等都是受控组件
input file 因为它的 value 只读,所以它是 React 中的一个非受控组件。
<input type="file" />
1 | constructor(props) { |
- 处理多个输入
当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作
1 | handleInputChange(event) { |
非受控组件
- 要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。
1 | class NameForm extends React.Component { |
- 默认值
受控组件中使用value给组件加上默认值,如果在非受控组件中使用
defaultValue或defaultChecked来设置默认值
1 | <input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked, |
文件输入本身就是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。
1 | class FileInput extends React.Component { |
8) 状态提升
- 通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的
共同父组件中去
1 | <TemperatureInput |
9) 组合 vs 继承
vue中通过slot的方式分发
匿名和具名slot,react中通过组件可以接受任意 props,包括基本数据类型,React 元素以及函数。包含关系
有点类似vue中的匿名slot插槽,有些组件无法提前知晓它们子组件的具体内容,组件使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中
1 | function FancyBorder(props) { |
还有
具名插槽的类似使用,将一个react元素传递给props属性
1 | function SplitPane(props) { |
10) Render Props
可以看到上面这种props直接传递一个react元素的方式,可以共享代码,术语 “render prop” 是指一种在 React 组件之间
使用一个值为函数的 prop 共享代码的简单技术
渲染属性指的是使用一个值为函数的prop来传递需要动态渲染的nodes或组件。如下面的代码可以看到我们的 DataProvider组件包含了所有跟状态相关的代码,而 Cat组件则可以是一个单纯的展示型组件,这样一来 DataProvider就可以单独复用了
使用函数的好处就是可以传递参数
1 | class DataProvider extends React.Component { |
11) Refs
- ref字符串的方式已经
移除了,之前是this.refs[‘refName’] 获取绑定在组件上的ref属性
回调refs
- 你会传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问
1 | class CustomTextInput extends React.Component { |
- ref回调传递
1 | function CustomTextInput(props) { |
react 16之后的获取ref的方法
- ref在函数式组件上不可使用,函数式组件无实例,但是其
内部的dom节点和类组件可以使用
1 | function CustomTextInput(props) { |
Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧
在React 16.3版本后,使用
React.createRef方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例
1 | class AutoFocusTextInput extends React.Component { |
- React 16.3版本后提供的
React.forwardRef,可以用来创建子组件,以传递ref
通过这样的方式就可以通过ref传递多个组件,获取组件内部的dom结构或者实例
1 | //子组件(通过forwardRef方法创建) |
- 高阶组件中ref的透传
1 | function logProps(Component) { |
13) 高阶组件
高阶组件是参数为组件,返回值为新组件的函数const EnhancedComponent = higherOrderComponent(WrappedComponent);
高阶组件这个概念就更好理解了,说白了就是一个函数接受一个组件作为参数,经过一系列加工后,最后返回一个新的组件
1 | const EnhancedComponent = function higherOrderComponent(WrappedComponent){ |
14) context上下文
- Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
- 这个方法用来创建context对象,并包含Provider、Consumer两个组件
数据的生产者,通过value属性接收存储的公共状态,来传递给子组件或后代组件
1 | <Provider value={/* some value */}> |
数据的消费者,通过订阅Provider传入的context的值,来实时更新当前组件的状态
1 | <Consumer> |
const {Provider, Consumer} = React.createContext(defaultValue);
1 | // Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。 |
值得一提的是每当Provider的值发生改变时, 作为Provider后代的所有Consumers都会重新渲染。为了防止 consumers 组件中触发意外的渲染,
将 value 状态提升到父节点的 state 里,除非通过setState改变value在内存中的指向,否则不会触发consumer中组件的渲染。本质就是在最外层的组件上,通过生产者Provider组件进行包裹,并存储共享数据到value中,当然可以是任何数据类型。后带需要用到共享数据的组件均可通过Consumer进行数据获取。
15) Fragments
- React 中的一个常见模式是一个组件返回多个元素。常用的解决办法无非两种:
Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点
1 | render() { |
返回一个数组
1 | render() { |
16)hook介绍
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
Hook 全家福
1 | 1)Basic Hooks |
- Hook 使用规则
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
1) 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
2) 只能在 React 的函数组件中调用Hook。不要在其他 JavaScript 函数中调用(自定义 Hook 除外)。
1 | //ESLint 插件 可以用来强制执行这两条规则 npm install eslint-plugin-react-hooks |
State Hook
useState 就是一个 Hook,通过在函数组件里调用它来给组件添加一些内部 state
更新状态的函数就类似 class 组件的 setState方法, 但是更新新状态的函数不会将新的 state 和旧的 state 合并, 而是直接替换旧的 state。
useState 会返回一对值:
当前状态和一个让你更新它的函数,是作为 state 的初始值, 只在第一次渲染的时候用到。可以是 number, boolean, string, object 的值, 如果初始值需要而另外的计算也可以是一个 function
出参 :[stateName, setStateFun] stateName => 当前的状态; setStateFun => 更改这个状态的函数
1 | import React, { useState } from 'react'; |
Effect Hook
你之前可能已经在 React 组件中执行过数据获取、订阅或者手动修改过 DOM。我们统一把这些操作称为“副作用”,或者简称为“作用”。
useEffect 就是一个 Effect Hook,给函数组件增加了操作
副作用的能力跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API
调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数。默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候
1 | useEffect(()=>{ |
- 为什么要在 effect 中返回一个函数?
这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。
- 通过跳过 Effect 进行性能优化
如果你传入了一个空数组([]),effect 内部的 props 和 state 就会一直拥有其初始值。尽管传入 [] 作为第二个参数更接近大家更熟悉的 componentDidMount 和 componentWillUnmount 思维模式,但我们有更好的来避免过于频繁的重复调用 effect。
1 | useEffect(() => { |
自定义 Hook
通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。