Skip to content
返回

React知识点总结

1

目录

点击展开

React

react 采用 monorepo 为了更方便的进行版本管理,依赖管理

React jsx

  Babel进行编译,借助 @babel/plugin-transform-react-jsx,,转换后生成 React.creactElement方式创建虚拟dom(VNode)

注意点

  1. 修改组件中的状态(state)时不能直接修改,需要生成新的对象使用setState方法去修改,可以通过use-immer库简化操作

  2. hook(例如useState)函数必须在函数组件的最顶层调用,不能在循环、条件判断或者嵌套函数中调用

  3. 组件初次执行以及组件(或者其祖先之一)的 状态发生了改变会触发一次渲染

  4. 当你调用 useState 时,React 会为你提供该次渲染 的一张 state 快照 (旧与新的函数组件对比)

  5. 函数式组件对state进行保留当判断组件显示隐藏 组件的UI位置没改变 就保留组件state; 函数式组件的UI位置改变 就销毁组件重置state

  例如:

//会重置 第二个Counter组件
<div>
  <Counter />
  {showB && <Counter />}
</div>
//会保留 Counter组件的state
<div>
  {isFancy ? (
   <Counter isFancy={true} />
 ) : (
   <Counter isFancy={false} />
 )}
</div>
// 只要在相同位置渲染的是相同组件, React 就会保留状态

Hooks

Hooks 最初是在 React 16.8 中引入的

  1. useState(initialState) // 设置函数式组件的状态,返回一个状态、修改状态的函数

  例如 const [count, setCount] = useState(0)

      setCount(1) // 设置 count 的值为 1

       参数可以是任何类型的值

       当设置参数为函数时 函数(更新函数)的第一个参数是当前的 state 例如 setCount(count => count + 1)

       更新函数必须是 纯函数 并且只 返回 结果

1. 修改对象时
//当状态为对象时 修改对象属性时,不要直接通过 点 . 的方式,而是应该塞入一个新对象。
  const [shape, setShape] = useState({
 color: 'orange',
 position: {
   x: 0,
   y: 0
 }
  });

  setShape({
 ...shape,
 position: {
   x: 1,
   y: 2
 }
  });
2. 修改数组时  
不能改变原数组
const [items, setItems] = useState([]);
  //添加元素
  setItems( // 替换 state
 [ // 是通过传入一个新数组实现的
   ...items, // 新数组包含原数组的所有元素
   { id: nextId++, name: name } // 并在末尾添加了一个新的元素
 ]
  );
  //删除元素
  setItems( // 替换 state
 items.filter(item => item.id !== id) // 通过 filter 方法创建一个新数组
  );
  //转换数组
  //如果你想改变数组中的某些或全部元素,你可以用 map() 根据条件判断创建一个新数组。
  //向数组中插入元素
  //数组展开运算符 ... 和 数组的 slice() 方法一起使用,方法让你从数组中切出“一片”。为了将元素插入数组
  const nextItems = [
 ...items.slice(0, 1),  // 插入点之前的元素:
 { id: nextId++, name: name }, // 新的元素:
 ...items.slice(1)// 插入点之后的元素:
  ];
  setItems(nextItems);
  1. useReducer(reducer, initialArg, init?)  

   用于简化useState逻辑操作的函数,与数组的reduce()方法类似

import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
 const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...
}

/*
  useReducer() 函数可接受三个参数:
  5. reducer 函数,用于处理初始值以及新值的state 和 调用dispatch()方法传来的 action,返回新的state
  6. 初始值,可以是对象、数组、数字、字符串等任意类型,用于初始化state
  7. 可选参数 init,如果存在,使用 init(initialArg) 的执行结果作为初始值

  useReducer() 返回值解析
  8. 数组的第一个元素是当前state
  9. 第二个元素是dispatch()方法,用于触发reducer函数

  注意点
  严格模式<StrictMode>下 React 会 调用两次 reducer 和初始化函数
  dispatch 函数调用后 是为下一次渲染而更新 state, 在此代码下面拿不到最新值
  https://react.docschina.org/reference/react/useReducer#troubleshooting
  不应该包含异步请求、定时器或者任何副作用(对组件外部有影响的操作)

  执行流程
 React 会把当前的 当前的state 和这个 dispatch传入的参数 action 一起作为参数传给 reducer 函数,然后 reducer 计算并返回新的 state,最后 React 保存新的 state,并使用它渲染组件和更新 UI。
*/
  1. useContext(SomeContext)    用于深层组件之间传值; 传统的办法是使用 props,一层一层把数据向下传递    SomeContext:createContext方法的返回值(上下文对象)

   用法:

import { useContext, createContext } from 'react';

const ThemeContext = createContext('light'); // 参数传入任意类型的值 当做传递默认值

function App() {
const [theme, setTheme] = useState('dark');
const [currentUser, setCurrentUser] = useState({ name: 'Taylor' });

// ...

return (
  // value 不传 会使用createContext() 方法传入的默认值
  <ThemeContext.Provider value={theme}>
 <AuthContext.Provider value={currentUser}>
   <Button />
 </AuthContext.Provider>
  </ThemeContext.Provider>
);
 }

 function Button() {
const theme = useContext(ThemeContext); // 接收
// ...
 }

理解: 可以想象 使用createContext 返回的上下文对象 进行派发值,然后在组件中使用useContext 获取并使用派发值;

  1. useEffect(setup, dependencies?)

  用于处理副作用,比如网络请求、DOM 操作、定时器、订阅事件、和外部变量进行交互等。

  可以理解是 React类组件的componentDidMount、componentDidUpdate、componentWillUnmount 的生命周期组合体。  

  1. setup 函数: 处理副作用的函数,函数内部可以返回一个清理函数 执行时机:        - 首次渲染时,最新dom树渲染完成后,此时会拿到最新的props和state        - 组件更新时,不传依赖项参数时,组件更新 setup函数会再次执行        - 组件销毁时
import { useEffect } from 'react';

useEffect(() => {// setup 函数
return  () => {// 清理函数   清理函数 第一次不执行,当前组件再次更新或销毁组件之前执行
}
})

      - async/await 使用方式

  useEffect(() => {
 ;(async () => {
   await fetch('xxx')
 })()
  })
  1. dependencies可选参数:  依赖项数组, setup函数的代码中引用的所有响应式值(props、state)的列表数组。       当传入空数组时,setup函数只会在首次渲染时执行,不会在组件更新时执行。       注意点:
    • dependencies参数不是数组时,会出现警告
  import { useEffect } from 'react';
  useEffect(() => {// setup 函数
 return  () => {// 清理函数
 }
  }, [])
      当传入依赖项数组时,setup函数会在首次渲染时执行,当依赖项数组中的值发生变化时,setup函数会再次执行。
import { useEffect, useState } from 'react';

const [count, setCount] = useState(0);
useEffect(() => {// setup 函数
return  () => {// 清理函数
}
}, [count])

     - Effect是如何模拟生命周期的? ```jsx import { useEffect,useState } from ‘react’;

const [count, setCount] = useState(() => {

console.log(‘getDerivedStateFromProps’) return 0; }); useEffect(() => { // TODO: 请求数据 console.log(‘componentDidMount’) return () => { //TODO: 解除事件监听 } }, [])

useEffect(() => {

console.log(‘componentWillReceiceProps’) },[count])

useEffect(() => {
console.log('componentDidUpdate')
})
```

7. useRef(initialValue)     可用于获取dom或者定义一个不会触发渲染的响应式变量

  initialValue:ref 对象的 current 属性的初始值。可以是任意类型的值。这个参数在首次渲染后被忽略

  useRef 返回值只有一个属性的对象:

    - current:ref 对象的当前值。不参与页面的响应式渲染,禁止在标签中使用

  用法:

import { useRef } from 'react'
const countRef = useRef(0) // countRef 是一个对象 {current: 0}

  注意点:

    - 改变 ref中的current属性的值, 不会触发组件重新渲染。

    - 相当于定义了一个普通变量

  1. useMemo(callback, dependencies)    用于缓存计算结果,避免重复计算。(与vue中的computed功能类似)用于性能优化    callback:一个函数,会根据依赖项数组中的值,返回一个缓存结果。    dependencies:依赖项数组,当依赖项数组中的值发生变化时,callback函数会重新执行。    返回值:callback函数的返回值。    - 跳过代价昂贵的重新计算
import { useMemo } from 'react';

function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
 // ...
}

      默认情况下,React 会在每次重新渲染时重新运行整个组件。       如果计算速度很快,这将不会产生问题。但是,当正在过滤转换一个大型数组,或者进行一些昂贵的计算,而数据没有改变,那么可能希望跳过这些重复计算。

      如果 todos 与 tab 都与上次渲染时相同,那么像上面那样将计算函数包装在 useMemo 中,便可以重用已经计算过的 visibleTodos。

      这种缓存行为叫做 记忆化

   - 跳过组件的重新渲染

    默认情况下,当一个组件重新渲染时,React 会递归地重新渲染它的所有子组件。     用 useMemo 和 memo 跳过重新渲染     子组件

 import { memo } from 'react';
 const List = memo(function List({ items }) {
// ...
 });

父组件

export default function TodoList({ todos, tab, theme }) {
// 告诉 React 在重新渲染之间缓存你的计算结果...
const visibleTodos = useMemo(
 () => filterTodos(todos, tab),
 [todos, tab] // ...所以只要这些依赖项不变...
);
return (
 <div className={theme}>
{/* ... List 也就会接受到相同的 props 并且会跳过重新渲染 */}
<List items={visibleTodos} />
 </div>
);
}

 或者记忆单个jsx节点

export default function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
const children = useMemo(() => <List items={visibleTodos} />, [visibleTodos]);
return (
 <div className={theme}>
{children}
 </div>
);
}
  1. useCallback(callback, dependencies)   用于缓存回调函数,避免重复创建。(弥补useMemo对于函数处理嵌套过深)   callback:想要缓存的函数。   dependencies:依赖项数组,当依赖项数组中的值发生变化时,callback函数会重新执行,返回一个新的函数引用。   返回值:返回你已经传入的 callback 函数,依赖项(dependencies)改变会返回新的

export default function Page({ productId, referrer }) {
 const handleSubmit = useCallback((orderDetails) => {
   post('/product/' + productId + '/buy', {
  referrer,
  orderDetails
   });
 }, [productId, referrer]);
 return <Form onSubmit={handleSubmit} />;
}
  ```

Share this post on:

上一篇文章
Go语言基础
下一篇文章
谈薪技巧