likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 3

1. 前言

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

下篇包括:

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

2. 元素引用

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

Vue:

const inputRef = ref(null);

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

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

React:

const inputRef = useRef<HTMLInputElement>(null);

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

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

通过 .current 使用 ref 引用。

3. 跨级传值

Vue:

// 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:

// 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:

<template>
  <div>
    <teleport to="body">
      <p>hello</p>
    </teleport>
  </div>
</template>

React:

import { createPortal } from 'react-dom';

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

5. Suspense

Vue:

<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:

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:

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

React:

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()

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

React:useState()

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 组件能够直接进行网络请求的两个位置:

// 在事件处理函数中
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()

const count = ref(1);
const doubleCount = computed(() => count.value * 2);

React:useMemo(), useCallback()

// 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