React Native 从类组件到函数组件

1. 发展趋势

React Native社区中的趋势是朝向使用函数组件(Functional Components)和Hooks的方向发展,而不是使用类组件(Class Components)。

React Native自推出Hooks API以来,函数组件和Hooks的使用变得更加普遍和推荐。Hooks提供了更简洁、可读性更高的代码,并且在状态管理、生命周期等方面提供了更强大的功能。使用Hooks可以更容易地共享逻辑和状态逻辑,而无需使用类组件的复杂结构。

以下是一些关于为什么React Native社区更倾向于使用函数组件和Hooks的理由:

可读性和简洁性: 函数组件通常比类组件更短,更易于阅读和理解。Hooks的引入使得在函数组件中管理状态和副作用变得更为直观。

逻辑复用: Hooks使得逻辑的复用更加容易。你可以使用自定义Hooks将组件之间的逻辑进行抽象和共享,而不需要使用高阶组件或渲染属性。

更直观的副作用处理: 使用useEffect等Hooks来处理副作用相对于类组件中的生命周期方法更为直观。

更容易集成现代JavaScript特性: 使用函数组件和Hooks可以更容易地与ES6+和TypeScript等现代JavaScript特性集成。

更好的性能优化: Hooks使得React能够更好地进行性能优化,并且React Native的未来版本也更加注重性能。

2. Hooks vs Class

Hooks 提供了在函数组件中执行副作用和访问状态的能力。下面是一些常见的 Hooks 及其在类组件中对应的生命周期方法:

  1. useState - setState:
  • 函数组件: 使用 useState 来声明和更新状态。
  • 类组件: 使用 this.setState 来更新状态。
  1. useEffect - componentDidMount, componentDidUpdate, componentWillUnmount:

    • 函数组件: 使用 useEffect 来执行副作用,可以模拟生命周期方法的行为。
    • 类组件:
      • componentDidMount: 在组件挂载后调用。
      • componentDidUpdate: 在组件更新后调用。
      • componentWillUnmount: 在组件卸载前调用。
js 复制代码
useEffect(() => {
  // componentDidMount 和 componentDidUpdate 的逻辑
  return () => {
    // componentWillUnmount 的逻辑
  };
}, [dependencies]);
  1. useContext - contextType:

    • 函数组件: 使用 useContext 来访问 React 上下文。
    • 类组件: 使用 contextType 来访问 React 上下文。
  2. useReducer - setStatethis.setState:

  • 函数组件: 使用 useReducer 来管理复杂状态逻辑。
  • 类组件: 可以使用 setState 或 this.setState,但 useReducer 更适合处理复杂状态逻辑。
  1. useCallback - shouldComponentUpdate:
  • 函数组件: 使用 useCallback 来记忆回调函数,以防止每次重新渲染时重新创建它。
  • 类组件: 在 shouldComponentUpdate 中进行优化,防止不必要的渲染。
  1. useMemo - shouldComponentUpdate:
  • 函数组件: 使用 useMemo 来记忆计算昂贵的计算结果。
  • 类组件: 在 shouldComponentUpdate 中进行优化,防止不必要的渲染。

这些是一些常见的 Hooks,它们为函数组件提供了类似于类组件生命周期方法的功能。使用 Hooks 可以更清晰地组织和重用逻辑。需要注意的是,使用 Hooks 时,每个 Hook 都是相互独立的,而类组件中的生命周期方法可能会涵盖多个生命周期阶段。

2.1 useState

useState 是 React Hooks 中用于在函数组件中添加状态的关键之一。它允许你在函数组件中添加和管理状态,而不需要使用类组件。useState 返回一个数组,其中包含两个元素:当前状态的值和一个更新状态的函数。

下面是 useState 的基本用法:

js 复制代码
import React, { useState } from 'react';

function MyComponent() {
  // 使用 useState 定义一个状态变量,初始值为 'initialValue'
  const [state, setState] = useState('initialValue');

  return (
    <div>
      <p>Current State: {state}</p>
      {/* 使用 setState 函数来更新状态 */}
      <button onClick={() => setState('newValue')}>
        Update State
      </button>
    </div>
  );
}

export default MyComponent;

在上面的例子中,useState 被调用并传入初始状态 'initialValue',返回的数组包含当前状态值 state 和更新状态的函数 setState。通过点击按钮,可以触发 setState 函数来更新状态。

useState 的基本用法包括:

  • 定义状态: 调用 useState 来声明一个状态变量,并传入初始值。
  • 获取当前状态值: 使用返回的数组的第一个元素(例如,state)来获取当前状态的值。
  • 更新状态: 使用返回的数组的第二个元素(例如,setState)来更新状态。setState 函数接受新的状态值作为参数。

请注意,useState 可以在组件中多次调用,以添加多个状态变量。每个状态变量都有自己独立的 setState 函数。

js 复制代码
import React, { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

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

      <p>Text: {text}</p>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
    </div>
  );
}

export default MyComponent;

在上面的例子中,MyComponent 组件有两个状态变量 counttext,每个都有自己的 setState 函数。

2.2 useEffect

useEffect 是 React Hooks 中用于处理副作用的关键之一。它在函数组件中提供了执行副作用的能力。useEffect 接受两个参数:

  • 一个函数,包含需要执行的副作用代码。
  • 一个可选的依赖数组,用于指定在依赖项发生变化时才重新运行 useEffect。
js 复制代码
import React, { useEffect } from 'react';

function MyComponent() {
  // 在组件挂载后执行一次
  useEffect(() => {
    // 执行副作用的代码

    // 清理函数(componentWillUnmount 的替代)
    return () => {
      // 在组件卸载前执行清理逻辑
    };
  }, [dependencies]); // 依赖项数组

  return (
    // 组件的 JSX
  );
}

export default MyComponent;
  • Effect 函数: 第一个参数是一个函数,包含需要在每次渲染后执行的副作用代码。这个函数可以返回一个清理函数(cleanup function),用于在组件卸载时执行清理逻辑,类似于 componentWillUnmount。
  • 依赖项数组 : 第二个参数是一个数组,包含影响副作用执行的变量。如果依赖项数组中的任何一个变量发生变化,useEffect 就会重新运行。如果省略依赖项数组,副作用将在每次组件渲染时都运行。
    以下是一些 useEffect 的常见用法:
  1. 只在组件挂载时执行副作用
js 复制代码
useEffect(() => {
  // 执行副作用的代码

  return () => {
    // 在组件卸载前执行清理逻辑
  };
}, []);
  1. 在特定依赖项变化时执行副作用
js 复制代码
useEffect(() => {
  // 执行副作用的代码

  return () => {
    // 在特定依赖项变化时执行清理逻辑
  };
}, [dependency1, dependency2]);
  1. 执行异步操作
js 复制代码
useEffect(() => {
  const fetchData = async () => {
    try {
      // 异步操作,比如从 API 获取数据
      const result = await fetchDataFromApi();
      // 处理结果
    } catch (error) {
      // 处理错误
    }
  };

  fetchData();

  return () => {
    // 在组件卸载前执行清理逻辑
  };
}, [dependency]);

useEffect 的使用取决于具体的需求,可以根据需要执行副作用,并确保在组件卸载前进行必要的清理。

2.3 useContext

useContext 是 React Hooks 中用于访问 React 上下文的钩子。它允许你在函数组件中订阅 React 上下文的值,而无需使用 Context.Consumer

下面是 useContext 的基本用法:

  1. 创建上下文: 使用 React.createContext 创建一个上下文对象。
  2. 在顶层组件提供上下文的值: 使用 Context.Provider 在组件树的某个位置提供上下文的值。
  3. 在子组件中使用 useContext: 在需要访问上下文的子组件中使用 useContext 来获取上下文的值。

以下是一个简单的例子:

js 复制代码
import React, { createContext, useContext } from 'react';

// 创建一个上下文对象
const MyContext = createContext();

// 在顶层组件提供上下文的值
function MyProvider({ children }) {
  const contextValue = 'Hello from Context';

  return (
    <MyContext.Provider value={contextValue}>
      {children}
    </MyContext.Provider>
  );
}

// 在子组件中使用 useContext 获取上下文的值
function MyComponent() {
  const contextValue = useContext(MyContext);

  return <p>{contextValue}</p>;
}

// 在应用的顶层组件中使用 MyProvider 包裹子组件
function App() {
  return (
    <MyProvider>
      <MyComponent />
    </MyProvider>
  );
}

export default App;

如果组件分别在不同的组件中, 则可以定义一个文件导出定义的 context:

js 复制代码
import { createContext } from 'react';

const MyContext = createContext();

export default MyContext;

2.4 useReducer

useReducer 是 React Hooks 中用于管理复杂状态逻辑的钩子。它提供了一种可预测的方式来更新状态,尤其适用于处理具有多个可能操作的状态。使用 useReducer 时,你需要定义一个 reducer 函数,该函数负责处理不同的操作,并返回新的状态。

下面是 useReducer 的基本用法:

  1. 定义 reducer 函数: 创建一个接受当前状态和操作的函数,根据操作类型返回新的状态。reducer 函数的格式为 (state, action) => newState
  2. 使用 useReducer: 在组件中调用 useReducer 并传入 reducer 函数和初始状态。
  3. 得到当前状态和 dispatch 函数: useReducer 返回一个包含当前状态和 dispatch 函数的数组。
  4. dispatch 操作: 调用 dispatch 函数并传入一个操作对象,reducer 将根据操作类型来更新状态。
    以下是一个简单的例子:
js 复制代码
import React, { useReducer } from 'react';

// 定义 reducer 函数
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// 使用 useReducer
function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}

export default Counter;

在上面的例子中,reducer 函数接收当前状态和操作,根据操作类型更新状态。useReducer 返回一个包含当前状态和 dispatch 函数的数组。通过调用 dispatch 函数,并传入包含 type 属性的操作对象,可以触发 reducer 来更新状态。

useReducer 还支持传入一个可选的初始化函数,用于计算初始状态,例如:

js 复制代码
const initialState = { count: 0 };

const init = (initialState) => {
  return { count: initialState.count * 2 };
};

const [state, dispatch] = useReducer(reducer, initialState, init);

在这个例子中,init 函数接收初始状态,并返回实际的初始状态。这可以是有用的,例如在初始状态需要基于某些计算的情况下。

使用 useReducer 的主要优势在于它使得状态逻辑更加模块化,特别是当有多个操作可能影响状态时。

2.5 useCallback

useCallback 是 React Hooks 中用于记忆回调函数的钩子。它的主要作用是在依赖不变的情况下,返回一个记忆化的回调函数,避免在每次渲染时创建新的回调函数。这有助于优化性能,特别是在子组件中使用时。

基本的使用方式如下:

js 复制代码
import React, { useCallback } from 'react';

function MyComponent({ onClick }) {
  // 使用 useCallback 包裹回调函数
  const memoizedCallback = useCallback(
    () => {
      // 回调函数的逻辑
      console.log('Callback executed!');
    },
    // 依赖项数组
    [/* dependencies */]
  );

  return (
    <button onClick={memoizedCallback}>
      Click me
    </button>
  );
}

export default MyComponent;

在上面的例子中,useCallback 接收两个参数:一个回调函数和一个依赖项数组。它返回一个记忆化后的回调函数 memoizedCallback。如果依赖项数组中的值没有发生变化,memoizedCallback 将保持相同的引用,从而避免在每次渲染时重新创建回调函数。

常见的用法包括:

  1. 避免子组件的不必要渲染: 在将回调函数传递给子组件时,使用 useCallback 避免子组件不必要地重新渲染。
js 复制代码
const memoizedCallback = useCallback(
  () => {
    // 回调函数的逻辑
  },
  [/* dependencies */]
);

return <ChildComponent onClick={memoizedCallback} />;
  1. 作为依赖项传递给其他 Hook: 在使用其他 Hook 时,将 useCallback 的记忆化回调函数作为依赖项传递。
js 复制代码
useEffect(() => {
  // 使用 memoizedCallback 作为依赖项
  someHook(memoizedCallback);
}, [memoizedCallback]);
  1. 避免在依赖项变化时触发 effect: 在使用 useEffect 时,通过使用 useCallback 避免在依赖项变化时触发 effect。
js 复制代码
useEffect(() => {
  // 在 memoizedCallback 变化时执行 effect
}, [memoizedCallback]);

需要注意的是,过度使用 useCallback 可能会导致性能问题 ,因为每个记忆化的回调函数都需要额外的内存。因此,只在确实有性能问题或需要时使用 useCallback

2.6 useMemo

useMemo 是 React Hooks 中用于记忆化计算结果的钩子。它接受一个计算函数和依赖项数组,并返回计算结果的记忆化版本。这有助于避免在每次渲染时重新计算耗时的操作,提高性能。

基本的使用方式如下:

js 复制代码
import React, { useMemo } from 'react';

function MyComponent({ data }) {
  // 使用 useMemo 记忆化计算结果
  const memoizedResult = useMemo(
    () => {
      // 计算结果的逻辑
      console.log('Computing result...');
      return data.filter(item => item > 5);
    },
    // 依赖项数组
    [data]
  );

  return (
    <div>
      <p>Result: {memoizedResult}</p>
    </div>
  );
}

export default MyComponent;

在上面的例子中,useMemo 接收两个参数:一个计算函数和一个依赖项数组。它返回一个记忆化后的计算结果 memoizedResult。如果依赖项数组中的值没有发生变化memoizedResult 将保持相同的引用,从而避免在每次渲染时重新计算结果。

常见的用法包括:

  1. 优化渲染性能: 避免在每次渲染时都执行昂贵的计算,只在依赖项变化时重新计算。
js 复制代码
const memoizedResult = useMemo(
  () => {
    // 昂贵的计算逻辑
  },
  [/* dependencies */]
);
  1. 记忆化函数: 当需要在每次渲染时都返回一个新的函数,但只有在依赖项变化时才创建新函数时,可以使用 useMemo 记忆化函数。
js 复制代码
const memoizedFunction = useMemo(
  () => {
    return () => {
      // 函数的逻辑
    };
  },
  [/* dependencies */]
);
  1. 避免重复计算: 当某个计算结果仅在特定依赖项变化时才发生变化时,使用 useMemo 避免重复计算。
js 复制代码
const memoizedResult = useMemo(
  () => {
    // 仅在特定依赖项变化时才计算结果
  },
  [specificDependency]
);

需要注意的是,过度使用 useMemo 可能会导致性能问题 ,因为每个记忆化的结果都需要额外的内存。因此,只在确实有性能问题或需要时使用 useMemo

相关推荐
番茄小酱0019 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
少恭写代码2 天前
duxapp放弃了redux,在duxapp中局部、全局状态的实现方案
react native·taro·redux·duxapp
番茄小酱0012 天前
ReactNative中实现图片保存到手机相册
react native·react.js·智能手机
EBABEFAC3 天前
响应式编程-reactor
java·开发语言·react native
Engss7 天前
Taro React-Native Android apk 打包
android·react native·taro
镰刀出海9 天前
RN开发环境配置与Android版本app运行
android·react native
wills77710 天前
Flutter 状态管理框架Get
flutter·react native
MavenTalk11 天前
前端跨平台开发常见的解决方案
前端·flutter·react native·reactjs·weex·大前端
起司锅仔11 天前
ReactNative TurboModule(3)
android·javascript·react native·react.js
起司锅仔11 天前
ReactNative 简述(1)
android·javascript·react native·react.js