前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Hooks:尽享React特性 ,重塑开发体验

Hooks:尽享React特性 ,重塑开发体验

作者头像
奋飛
发布2024-05-25 19:32:39
470
发布2024-05-25 19:32:39
举报
文章被收录于专栏:Super 前端Super 前端

🎼 React 16.8 版本引入了 Hooks ,可以在不使用 Class 的情况下使用 React 特性。

Hooks 允许从函数组件 “hook into” React 状态和生命周期特性。

代码语言:javascript
复制
function Counter () {
  const [count, setCount] = useState(0);
  
  return (<>
  	<p>{count}</p>
    <button onClick={() => setCount(count + 1)}> +1 </button>
  </>)
}
为什么引入 Hooks ?

一个新的方案引入,一定是为了解决现存的问题。对于 Hooks 来说,就是为解决 Class 的诟病。

  • 组件之间复用状态逻辑异常困难,存在“回调地狱”的风险 ==> render props 和 Hoc 高阶组件都需要重新构造组件。
  • 复杂的组件难以理解及维护(状态逻辑及副作用堆积)==> 常见的,每个生命周期方法中包含了一组不相关的逻辑。
  • 基于 Class 的组件,比较难以理解,且不能很好的控制范围,对于热更新不友好,优化路径不佳。

因此,引入了 Hooks:

  • 使用 Hooks 可以从组件中提取有状态逻辑,这样就可以独立地对其进行测试并复用。其允许在不改变组件层次结构的情况下复用有状态逻辑。这样可以很容易在许多组件之间或与社区共享 Hook。
  • 使用 Hooks 可以将一个组件拆分为更小的函数,而不是强制基于生命周期方法进行拆分。也可以选择使用 reduce 来管理组件的本地状态,以使其更可预测。
  • Hooks 允许在不使用类的情况下更多地使用 React 的特性。从概念上讲,React 组件总是更接近于函数,不需要学习复杂的功能或响应式编程技术。

Hooks 是否可以完全取代 render props 和 Hoc 组件?1 答:不能,例如虚拟滚动组件需要具有 renderItem prop,以及可视化容器组件可能具有自己的DOM结构。

✔️ Hooks 让我们根据代码所做的,而不是生命周期方法名称来分割代码。React 组件一直更像是函数,而 Hooks 则拥抱了函数。

Hooks 使用规则(调用位置有限制)

✅ 在函数组件的顶层调用 Hooks ✅ 在 React 的函数组件或自定义Hooks中调用 Hook

下述以 useState(React 内置钩子) 为例:

代码语言:javascript
复制
// ✅ 在函数组件的顶层调用
function Counter () {
  const [count, setCount] = useState(0);
}

// ✅ 在自定义Hooks的顶层调用
function useWindowWidth () {
  const [width, setWidth] = useState(window.innerWidth);
}

不要在循环、条件、嵌套函数 或 try/catch/finally 块中调用。这样可以做到各个 Hook 在每一次渲染中,调用的顺序是一致的。

代码语言:javascript
复制
const [count, setCount] = useState(0);

数组结构语法允许我们为状态变量赋予不同的名称。这些名称不是 useState API 的一部分。

替代生命周期
  • constructor: 函数组件不需要 constructor,可以通过 useState 初始化(如果数据复杂,可以传入函数);
  • getDerivedStateFromProps:渲染过程更新
  • shouldComponentUpdate:使用 React.memo
  • componentDidMount, componentDidUpdate, componentWillUnmount:Effect Hooks 可以替代

示例:Class 形式:

代码语言:javascript
复制
class FriendStatusWithCounter extends React.Component {
  constructor(props) {
    super(props);
  }
  
  // 初始化:订阅
  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(...);
  }

  // DOM更新:先取消再重新订阅
  componentDidUpdate(prevProps) {
    ChatAPI.unsubscribeFromFriendStatus(...);
    ChatAPI.subscribeToFriendStatus(...);
  }

  // 卸载:取消订阅
  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(...);
  }
}

Hooks 形式:

代码语言:javascript
复制
function FriendStatus(props) {
  useEffect(() => {
    // 订阅
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      // 取消
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    }
	})
}
如何获取 previous props 或 state ?
代码语言:javascript
复制
useEffect(() => {
  ChatAPI.subscribeToFriendStatus(props.friend.id);
  return () => ChatAPI.unsubscribeFromFriendStatus(props.friend.id);
}, [props.userId]);

在上面的示例中,如果 Id 从 3 => 4,ChatAPI.unsubscribeFromFriendStatus(3) 将首先运行,然后 ChatAPI.subscribeToFriendStatus(4) 将运行。不需要获取 “previous Id”,因为 clean up 函数将在闭包中捕获它。

➰或者可以将以前的 state 或 props 存储。

代码语言:javascript
复制
function ScrollView({row}) {
  const [isScrollingDown, setIsScrollingDown] = useState(false);
  // 存储之前值
  const [prevRow, setPrevRow] = useState(null);

  if (row !== prevRow) {
    // 自上次渲染后更改
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }

  return `Scrolling down: ${isScrollingDown}`;
}
内置 Hooks
State Hook2

可以用于记住用户输入的信息。

Context Hook

从祖先组件接收信息,而无需将其作为 props 传递。

代码语言:javascript
复制
// ① 创建context
const ThemeContext = createContext(null);

export default () => {
    const [color, setColor] = useState('red');
    return (<>
        {/* ② 使用 context provider 进行包裹 */}    
        <ThemeContext.Provider value={color}>
            <MyText />
        </ThemeContext.Provider>
    </>)
}

function MyText() {
  	// ③ 使用
    const color = useContext(ThemeContext);
    return (<>
        <div>{color}</div>
    </>)
}
Ref Hook 3

保存一些不用于渲染的信息,比如 DOM 节点或 timeout ID。

  • 使用 useRef 声明 ref。你可以在其中保存任何值,但最常用于保存 DOM 节点。
  • 使用 useImperativeHandle 自定义从组件中暴露的 ref,但是很少使用。
Effect Hook 4

连接到外部系统并与之同步。这包括处理网络、浏览器、DOM、动画、使用不同 UI 库编写的小部件以及其他非 React 代码。

  • 使用 useEffect 将组件连接到外部系统。
  • useLayoutEffect 在浏览器重新绘制屏幕前执行,可以在此处测量布局。
  • useInsertionEffect 在 React 对 DOM 进行更改之前触发,库可以在此处插入动态 CSS。
性能 Hook

优化重新渲染性能的一种常见方法是跳过不必要的工作。例如,可以告诉 React 重用缓存的计算结果,或者如果数据自上次渲染以来没有更改,则跳过重新渲染:

  • 使用 useMemo 缓存计算代价昂贵的计算结果。
  • 使用 useCallback 将函数传递给优化组件之前缓存函数定义。

将必须同步的阻塞更新(比如使用输入法输入内容)与不需要阻塞用户界面的非阻塞更新(比如更新图表)分离以提高性能:

  • useTransition 允许将状态转换标记为非阻塞,并允许其他更新中断它。
  • useDeferredValue 允许延迟更新 UI 的非关键部分,以让其他部分先更新。
其他 Hook
  • 使用 useDebugValue 自定义 React 开发者工具为自定义 Hook 添加的标签。
  • 使用 useId 将唯一的 ID 与组件相关联,其通常与可访问性 API 一起使用。
  • 使用 useSyncExternalStore 订阅外部 store。
  1. https://legacy.reactjs.org/docs/hooks-faq.html#which-versions-of-react-include-hooks react hook FAQ ↩︎
  2. https://blog.csdn.net/ligang2585116/article/details/136458885 总结:React 中的 state 状态 ↩︎
  3. https://blog.csdn.net/ligang2585116/article/details/136626405 脱围:使用 ref 保存值及操作DOM ↩︎
  4. https://blog.csdn.net/ligang2585116/article/details/136880009?spm=1001.2014.3001.5501 Effect:由渲染本身引起的副作用 ↩︎
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么引入 Hooks ?
  • Hooks 使用规则(调用位置有限制)
    • 替代生命周期
      • 如何获取 previous props 或 state ?
        • 内置 Hooks
          • State Hook2
          • Context Hook
          • Ref Hook 3
          • Effect Hook 4
          • 性能 Hook
          • 其他 Hook
      相关产品与服务
      云开发 CLI 工具
      云开发 CLI 工具(Cloudbase CLI Devtools,CCLID)是云开发官方指定的 CLI 工具,可以帮助开发者快速构建 Serverless 应用。CLI 工具提供能力包括文件储存的管理、云函数的部署、模板项目的创建、HTTP Service、静态网站托管等,您可以专注于编码,无需在平台中切换各类配置。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档