# React-learning

# State

  1. 不要直接修改 State

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

  3. State 的更新会被合并

为什么会有 setState,setState 做了什么?

vue 是对数据添加监听,自动响应到 dom 上,setState 是更新数据的同时,更新 dom

这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。

// 构造器中绑定
this.handleClick = this.handleClick.bind(this)
// 以下为class fields实验性语法
this.handleClick = () => { console.log(this) }

// 不推荐,可能会造成子组件性能问题
  <button onClick={() => this.handleClick()}>Click</button>

// 两种等价,第一种事件对象必须显示指定
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
class Comp extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return <div></div>
  }
}

表单受控组件

预留字段:

key

props.children 默认 slot,也可以指向 slot-name,注意 react 中没有槽 slot 概念,一切可以使用 props 传递

left = {
  <div></div>
}

# React 哲学

# 第一步:将设计好的 UI 划分为组件层级

可以将组件当作一种函数或者是对象来考虑,根据单一功能原则来判定组件的范围。

组件设计也符合设计模式原则

# 第二步:用 React 创建一个静态版本

最好将渲染 UI 和添加交互这两个过程分开。

不应该使用 state 构建静态版本,state 代表了随时间会产生变化的数据。

对于较为大型的项目来说,自下而上地构建,并同时为低层组件编写测试是更加简单的方式。

React 单向数据流(也叫单向绑定)的思想使得组件模块化,易于快速开发。

组件即函数,props=函数形参,state=函数内变量

函数式编程思想

# 第三步:确定 UI state 的最小(且完整)表示

确定是否是 state:由用户行为触发改变且不依赖其它 state 或 props 的数据

# 第四步:确定 state 放置的位置

单向数据流,顺着组件层级从上往下传递,状态提升。

状态提升:有状态的组件没有渲染,有渲染的组件没有状态。

# 第五步:添加反向数据流

React 通过一种比传统的双向绑定略微繁琐的方法来实现反向数据传递。尽管如此,但这种需要显式声明的方法更有助于人们理解程序的运作方式。

函数式编程思想。另外确实不如 vue 双向绑定简洁。

由于 state 只能由拥有它们的组件进行更改,父组件可以将一个能够改变 state 的回调函数传递给子组件,从而更新应用。

语法:

import { Fragment, createRef } from 'react'

# 代码分割

# import()

代码分割最佳方式是动态 import()语法,webpack 解析到该语法时,会自动进行代码分割。如果使用 create-react-app,该功能开箱即用。

当使用 babel 时,要确保正确解析 import 语法而不是将其转换,需要使用 babel-plugin-sync-dynamic-import 插件

import("./math").then(math => {})

// .babelrc
{
  "plugins": ["sync-dynamic-import"]
}

# React.lazy()

import React, { Suspense } from 'react'

const OtherComponent = React.lazy(() => import('./OtherComponent'))

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  )
}

# 基于路由的代码分割

import React, { Suspense, lazy } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'

const Home = lazy(() => import('./routes/Home'))
const About = lazy(() => import('./routes/About'))

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path='/' component={Home} />
        <Route path='/about' component={About} />
      </Switch>
    </Suspense>
  </Router>
)

# 深入 JSX

# 在运行时选择类型

类似 vue 动态组件,在 JSX 中,不能将通用表达式作为 React 元素类型,需要先将它赋值给大写字母开头的变量,再渲染。

import React from 'react'
import { PhotoStory, VideoStory } from './stories'

const components = {
  photo: PhotoStory,
  video: VideoStory,
}

function Story(props) {
  // 正确!JSX 类型可以是大写字母开头的变量。
  const SpecificStory = components[props.storyType]
  return <SpecificStory story={props.story} />
}

# Portals

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el
    );
  }

虽然脱离 DOM 树,但依然在 React 树中。

# Render props

类似 vue slot

# Hook

组件复用很容易形成嵌套地狱

复杂组件变得难以理解

从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。

# Hook API 索引

# 基础 Hook

# useState

函数式更新

setCount((prevCount) => prevCount + 1)

useState不会自动合并对象,可以结合展开运算符来达到合并更新对象的效果。

useReducer 是另一种可选方案,它更适合用于管理包含多个子值的 state 对象。

惰性初始 state

只会在组件的初始渲染中起作用,后续渲染时会被忽略。

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props)
  return initialState
})

跳过 state 更新

React 使用 Object.is 比较算法 来比较 state。

如果你在渲染期间执行了高开销的计算,则可以使用 useMemo 来进行优化。

# useEffect

清除 effect

useEffect 可以返回一个清除函数。为防止内存泄漏,清除函数会在组件卸载前执行。如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除

细节:

  • Hook 使用了 JavaScript 的闭包机制。

  • React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

  • componentDidMountcomponentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 useLayoutEffect Hook 供你使用,其 API 与 useEffect 相同。

  • React 会在组件卸载的时候执行清除操作。effect 在每次渲染的时候都会执行,React 会在执行当前 effect 之前对上一个 effect 进行清除。

  • 多个 effect 调用顺序:第一个 effect 清除-第二个 effect 清除...-第一个 effect 执行-第二个 effect 执行...

  • child chear 1
    child 2
    clear 1
    clear2 1
    effect 2
    effect2 2
    
  • useEffect 第二个参数传入数组,可通过跳过 effect 进行性能优化,请确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量。参阅文档,了解更多关于如何处理函数以及数组频繁变化时的措施内容。Q:如果传错会怎样?引用到先前渲染中的旧变量是什么意思?

  • useEffect(() => {
      console.log('effect3', num) // effect相当于闭包 ??
    }, [count])
    

*

# Awesome-React

React

React 生命周期

了解 React 设计思想

webpack 中文文档

Create-React-App 官方脚手架

react-router

# Code Splitting

webpack-lazy-loading

loadable-vs-react-lazy/