了解了这些你就掌握了 React(下)

1. 前言

本文将以 React 和 Vue3 进行同步对比,带大家快速掌握 React。这个系列分为上下两篇,上篇内容包括:

  • 组件
  • JSX
  • 插值
  • 数据通信
  • 渲染
  • 事件
  • 状态

下篇包括:

  • 元素引用
  • 跨级传值
  • Portals
  • Suspense
  • Error Boundaries
  • Hooks

2. 元素引用

想要引用HTML元素节点,在 Vue 中通过 ref 来实现,React 通过 useRef 钩子。

Vue:

ts 复制代码
const inputRef = ref(null);

onMounted(() => {
  if (inputRef) {
    inputRef.value.focus();
  }
});

<input type="text" ref="inputRef" />

React:

ts 复制代码
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
  inputRef?.current?.focus();
}, []);

<input type="text" ref={inputRef} />

通过 .current 使用 ref 引用。

3. 跨级传值

Vue:

ts 复制代码
// App.vue
<script setup>
import { provide } from 'vue';
provide('theme', 'light');
</script>

<template>
  <ChildComponent />
</template>

// ChildComponent.vue
<script setup>
import { inject } from 'vue';
const theme = inject('theme');
</script>

<template>
  <div>当前主题:{{ theme }}</div>
</template>

React:

ts 复制代码
// 1. 创建一个Context
export const ThemeContext = createContext('light');

// 2. 在父组件(App.tsx)中使用 ThemeContext.Provider 包裹子组件,并传值
//<ThemeContext.Provider value="dark">
  <ChildComponent />
//</ThemeContext.Provider>

// ChildComponent.tsx
import { useContext } from 'react';
import { ThemeContext } from '../App'; // 3. 导入上下文

export default function ChildComponent() {
  // 使用 useContext 获取Context的值
  const theme = useContext(ThemeContext); // 4. 使用上下文中的数据

  return <div>当前主题:{theme}</div>;
}

如果父组件没有提供 <ThemeContext.Provider>,将会使用默认值 light。

4. Portals

传送门,Vue3提供了内置组件<Teleport>,而在 React 中使用的是 createPortal

Vue:

ts 复制代码
<template>
  <div>
    <teleport to="body">
      <p>hello</p>
    </teleport>
  </div>
</template>

React:

ts 复制代码
import { createPortal } from 'react-dom';

export default function App() {
	return (
		<div>
			{createPortal(<p>hello</p>, document.body)}
		<div>
	)
}

5. Suspense

Vue:

ts 复制代码
<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <p>Loading...</p>
    </template>
  </Suspense>
</template>

<script setup>
import { defineAsyncComponent, Suspense } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
  import('./AsyncComponent.vue')
);
</script>

React:

ts 复制代码
import React, { Suspense } from 'react';

const AsyncComponent = React.lazy(() => import('./AsyncComponent'));

function App() {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <AsyncComponent />
    </Suspense>
  );
}

export default App;

6. Error Boundaries

边界错误处理。

Vue:

ts 复制代码
app.config.errorHandler = (err, instance, info) => {
  // 处理错误,例如:报告给一个服务
}

React:

ts 复制代码
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新状态,以便下一次渲染将显示后备 UI。
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // 示例"组件堆栈":
    //   在 ComponentThatThrows 中(由 App 创建)
    //   在 ErrorBoundary 中(由 APP 创建)
    //   在 div 中(由 APP 创建)
    //   在 App 中
    logErrorToMyService(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // 你可以渲染任何自定义后备 UI
      return this.props.fallback;
    }

    return this.props.children;
  }
}

<ErrorBoundary fallback={<p>Something went wrong</p>}>
  <Profile />
</ErrorBoundary>

7. Hooks

7.1 State 状态

Vue:ref(), reactive()

ts 复制代码
const demoRef = ref('demo ref');
const demoRef2 = ref([1, 2, 3]);
const demoReactive = reactive({
  name: 'Alice',
  age: 18,
});

React:useState()

ts 复制代码
const [demoState, setDemoState] = useState();

7.2 Context 上下文

Vue:provide() + inject()

React:useContext()

7.3 Ref 引用

Vue:ref()

React:useRef()

7.4 Effect 副作用

Vue:无

React:useEffect() React 组件是比较"纯"的函数,它能保证输入和输出的一致性。与浏览器、API等外部系统等连接(如:网络请求、订阅事件、更新 DOM),要使用 useEffect() 钩子,而不是插入到组件的渲染中,这是跳出 React 的方式。

下面是 React 组件能够直接进行网络请求的两个位置:

ts 复制代码
// 在事件处理函数中
function onSubmit(e) {
	e.preventDefault();
	http.post('api/login', {...}).then(...).catch(...);
}
// 在 useEffect 中
useEffect(() => {
	const getData = async () => {
		fetch(url).then(...).catch(...)
	}
	getData();
},[])

7.5 Performance 性能优化

Vue:computed()

ts 复制代码
const count = ref(1);
const doubleCount = computed(() => count.value * 2);

React:useMemo(), useCallback()

ts 复制代码
// 1. useMemo()
// 接受一个函数和一个依赖项数组,它会在依赖项发生变化时重新计算函数的返回值,并将结果缓存起来。
import React, { useState, useMemo } from 'react';

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

    // 使用 useMemo 缓存计算结果
    const doubleCount = useMemo(() => {
        return count * 2;
    }, [count]); // 仅在 count 发生变化时重新计算

    return (
        <div>
            <button onClick={() => setCount(count + 1)}>Increment Count</button>
            <p>Count: {count}</p>
            <p>double Count: {doubleCount}</p>
        </div>
    );
};

// 2. useCallback()
// 用于缓存回调函数,避免在每次渲染时创建新的回调函数。
import { useState, useCallback } from 'react';

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

    // 使用 useCallback 缓存回调函数
    const increment = useCallback(() => {
        setCount(count + 1);
    }, [count]); // 仅在 count 发生变化时重新创建

    return (
        <div>
            <button onClick={increment}>Increment Count</button>
            <p>Count: {count}</p>
        </div>
    );
};

技术交流:

  • 公众号:见嘉 Being Dev
  • v:with_his_x
相关推荐
GISer_Jing3 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪3 小时前
CSS复习
前端·css
咖啡の猫5 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲8 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5818 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路8 小时前
GeoTools 读取影像元数据
前端
ssshooter9 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry10 小时前
Jetpack Compose 中的状态
前端
dae bal10 小时前
关于RSA和AES加密
前端·vue.js
柳杉10 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化