概述:
TL;DR:异步代码挂起渲染。
我有一个带有Modal的组件,在Modal中,它呈现了一个用户可以选择的过滤器列表。按下过滤器时,项目的颜色会发生变化,它会向数组中添加一个简单的代码(Number(问题是在将代码添加到数组的逻辑完成之前,颜色更改的呈现将挂起。
我不明白为什么在数组中添加一个数字需要一到两秒的时间。我不明白为什么渲染挂起,直到后面的整个逻辑完成。
注意:我有Vue的背景,这是我使用react/areact native的第一个项目。所以,如果我做错了什么,如果有人指出,我将不胜感激
复制问题的零食:
零食链接
我的参考代码:
我使用react native和expo-managed,并为UI使用一些本机基本组件。我不能分享整个代码源,但以下是导致问题的逻辑部分:
父级:FilterModal.js
渲染部分:
...
<Modal
// style={styles.container}
visible={modalVisible}
animationType="slide"
transparent={false}
onRequestClose={() => {
this.setModalVisible(!modalVisible);
}}
>
<Center>
<Pressable
onPress={() => this.setModalVisible(!modalVisible)}
>
<Icon size="8" as={MaterialCommunityIcons} name="window-close" color="danger.500" />
</Pressable>
</Center>
// I use sectionList because the list of filters is big and takes time to render on the screen
<SectionList
style={styles.container}
sections={[
{ title: "job types", data: job_types },
{ title: "job experience", data: job_experience },
{ title: "education", data: job_formation },
{ title: "sector", data: job_secteur }
]}
keyExtractor={(item) => item.id}
renderItem={({ item, section }) => <BaseBadge
key={item.id}
pressed={this.isPressed(section.title, item.id)}
item={item.name}
code={item.id}
type={section.title}
add={this.addToFilters.bind(this)}
></BaseBadge>}
renderSectionHeader={({ section: { title } }) => (
<Heading color="darkBlue.400">{title}</Heading>
)}
/>
</Modal>
...
逻辑部分:
...
async addToFilters(type, code) {
switch (type) {
case "job types":
this.addToTypesSelection(code);
break;
case "job experience":
this.addToExperienceSelection(code);
break;
case "formation":
this.addToFormationSelection(code);
break;
case "sector":
this.addToSectorSelection(code);
break;
default:
//TODO
break;
}
}
...
// the add to selection methods look something like this :
async addToTypesSelection(code) {
if (this.state.jobTypesSelection.includes(code)) {
this.setState({ jobTypesSelection: this.state.jobTypesSelection.filter((item) => item != code) })
}
else {
this.setState({ jobTypesSelection: [...this.state.jobTypesSelection, code] })
}
}
...
儿童:
渲染部分
render() {
const { pressed } = this.state;
return (
< Pressable
// This is the source of the problem and read further to know why I used the setTimeout
onPress={async () => {
this.setState({ pressed: !this.state.pressed });
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
<Badge
bg={pressed ? "primary.300" : "coolGray.200"}
rounded="md"
>
<Text fontSize="md">
{this.props.item}
</Text>
</Badge>
</Pressable >
);
};
预期结果:
setState({pressed:!this.state.pressed})
会立即完成项的渲染,其余代码会在渲染之后完成,并且不会挂起渲染。
使用向数组添加代码可以在后台更改父状态,但我需要过滤器项ui立即更改。
我尝试过的事情:
异步方法
我试着让这些方法异步,而不是等待它们,这样它们就可以异步发生。这并没有改变任何东西,而且看起来react native忽略了方法是异步的。它一直挂起,直到所有操作都完成,方法才更改父状态。
实施";事件发射侦听逻辑">
这是我选择使用response/react native的第一个应用程序,来自Vue。我想到了从子级发出事件,在父级上侦听它,并执行将代码添加到数组的逻辑。
这并没有改变任何事情,我使用了eventemitter3和react native event listener
使用超时
这是我最后一次绝望的尝试,让这个应用程序暂时可用,直到我弄清楚自己做错了什么。基本上,我在更改过滤器组件的状态后添加了一个Timeout,如下所示:
...
< Pressable
onPress={async () => {
// change the state this changes the color of the item ↓
this.setState({ pressed: !this.state.pressed });
// this is the desperate code to make the logic not hang the rendering ↓
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
...
感谢您的阅读,非常感谢您提供有用的答案和文档链接以及其他可以帮助我更好地理解的文章。
再说一次,我是反应/反应母语的新手,所以如果有一些概念我不理解,请给我指正确的方向。
对于任何读到这篇文章的人来说,我终于发现了问题所在,并能够为我解决这个问题。
渲染挂起的原因是,无论我是否使其异步,推送到我的数组的代码都需要时间——它是在主线程上执行的,而且这一变化触发了屏幕重新渲染,需要等待js逻辑完成。
有助于解决方案和改进的因素有:
- 使保存所选过滤器的数组(现在是一个map{}(成为无状态,换句话说,不要使用useState来声明数组,而是使用好的旧js,它不会触发任何屏幕重新渲染。当用户应用过滤器,然后像我一样将纯js对象推送到一个状态或上下文中并使用它时,这样做可以确保用户可以在不挂起交互的情况下垃圾邮件选择和取消选择过滤器
- 第一件事是让数组成为一个映射,这并不能解决重新发布的问题