如何从存储更新React Native函数式组件State并解决测试渲染异常
问题原因及解决方案
核心问题有三个,同时涉及组件写法和测试写法:
- JSON序列化丢失类实例特征:你通过
JSON.stringify存储MyClass实例,读取后JSON.parse得到的是普通JS对象,不是MyClass类的实例,没有getTitle()、getValue()方法,调用后返回undefined,所以渲染为空。
- JSON序列化丢失类实例特征:你通过
- 组件异步逻辑位置错误:直接在函数组件顶层调用异步加载逻辑,会导致组件每次重渲染都重复触发加载,正确写法应该放在
useEffect空依赖回调中,仅挂载时执行一次。
- 组件异步逻辑位置错误:直接在函数组件顶层调用异步加载逻辑,会导致组件每次重渲染都重复触发加载,正确写法应该放在
- 测试没有等待异步逻辑执行完成:
setup是异步函数,你没有await就直接渲染组件,且渲染后没有等待状态更新就调用debug(),拿到的是首次渲染的初始状态结果。
- 测试没有等待异步逻辑执行完成:
修复后的组件代码
import React, { useState, useEffect } from "react"; import { ReactElement } from "react-native/node_modules/@types/react"; import { Text, View } from "react-native"; import MyClass from "./MyClass"; import AsyncStorage from "@react-native-async-storage/async-storage"; export default function FunCompWithState(): ReactElement { const [myClass, setMyClass] = useState<MyClass>(new MyClass()); // 放到useEffect中,仅挂载时执行一次 useEffect(() => { async function load() { const data = await loadFromStorage(); if (data) { // 把普通对象转成MyClass实例 const instance = new MyClass(); instance.setTitle(data.title); instance.setValue(data.value); setMyClass(instance); } } load(); }, []); return ( <View> <Text>{myClass.getTitle()}</Text> <Text>{myClass.getValue()}</Text> </View> ); } async function loadFromStorage(): Promise<Record<string, any> | null> { const jsonValue = await AsyncStorage.getItem("@main.myClass"); console.log(jsonValue); if (jsonValue != null) { return JSON.parse(jsonValue); } return null; }
修复后的测试代码
import * as React from "react"; import { render, waitFor } from "@testing-library/react-native"; import FunCompWithState from "../FunCompWithState"; import MyClass from "../MyClass"; import AsyncStorage from "@react-native-async-storage/async-storage"; async function setup() { const storedClass = new MyClass(); storedClass.setTitle("Stored Class"); storedClass.setValue(8); await AsyncStorage.setItem("@main.myClass", JSON.stringify(storedClass)); } // 测试用例改为异步函数 it("Render FunCompWithState", async () => { // 等待初始化存储完成 await setup(); const { debug, getByText } = render(<FunCompWithState />); // 等待异步状态更新完成,确认目标文本渲染后再debug await waitFor(() => { expect(getByText("Stored Class")).toBeTruthy(); expect(getByText("8")).toBeTruthy(); }); debug(); });
内容的提问来源于stack exchange,提问作者Austin Brown
相关产品推荐
相关产品推荐

