编辑:这似乎是由于在进行UI更新的同一更新中调用API的一些问题:
https://github.com/facebook/react-native/issues/32867
https://github.com/facebook/react-native/issues/35037
我有一个巨大的UI/UX问题-我有一组按钮,我想尽快更新样式,然后我还有一些其他的组件,整个加载过程可能需要相当多的时间来完成。现在,按钮需要的时间和加载组件一样长,这是可怕的UX。
这两个组件都依赖于相同的数据-我的类别切片,如下所示:
import { createSlice } from "@reduxjs/toolkit";
const initialState = [];
export const categoriesSlice = createSlice({
name: "categories",
initialState,
reducers: {
categoriesSave: (state, action) => {
state = [...state, action.payload];
return state;
},
categoriesSaveAll: (state, action) => {
if (state === action.payload) {
return state;
}
return action.payload;
},
},
});
export const { categoriesSave, categoriesSaveAll } =
categoriesSlice.actions;
export default categoriesSlice.reducer;
我的类别组件,如下所示:
const CatsModalChild = (props: any) => {
const reduxCategories = useSelector(
(state: RootStateOrAny) => state.categories
);
const [localCategories, setLocalCategories] = useState<any[]>([]);
const categories = useCategoriesAPIQuery({ taxon: props.taxon });
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const categoryChange = (value: any) => {
let _selectedCategories = [...selectedCategories];
if (_selectedCategories.indexOf(value) === -1) {
_selectedCategories.push(value);
} else {
_selectedCategories = _selectedCategories.filter(
(item) => item !== value
);
}
setSelectedCategories(_selectedCategories);
};
const onCategoriesSelectionsChange = () => {
if (categories.status === "fulfilled") {
let selectedCats = selectedCategories.map((x: any) => x.value);
let deleteCats = new Set(categories.data.map((x: any) => x.value));
// @ts-ignore
const difference = localCategories.filter((x) => !deleteCats.has(x));
const cats = [...selectedCats, ...difference];
props.categoriesSaveAll(cats);
}
};
const categoriesMapper = () => {
let _selectedCategories: any = [];
for (let cat of localCategories) {
let catVal = categories.data.filter((item: any) => item.value === cat);
let selectedCat = catVal[0];
if (selectedCat) {
_selectedCategories.push(selectedCat);
}
}
setSelectedCategories(_selectedCategories);
};
// This was an unsuccessful attempt to decouple my redux store from my local state.
useEffect(() => {
const _localCategories = [...reduxCategories];
setLocalCategories(_localCategories);
}, []);
useEffect(() => {
onCategoriesSelectionsChange();
}, [selectedCategories, categories]);
useEffect(() => {
if (categories.data?.length > 0) {
categoriesMapper();
}
}, [categories]);
if (categories.status == "pending") {
return <Box />;
}
return (
<MultipleSelect
items={categories.data}
selectedItems={selectedCategories}
onPress={categoryChange}
/>
);
};
const mapStateToProps = (state: any) => {
... irrelevant now, moved all state out of here
}
function mapDispatchToProps(dispatch: any) {
let actions = bindActionCreators(
{
categoriesSaveAll,
},
dispatch
);
return { ...actions, dispatch };
}
export const CatsModal = connect(
mapStateToProps,
mapDispatchToProps
)(memo(CatsModalChild));
最后是我的文章组成部分:
const Articles = (props) => {
return (
<ScrollView
minH={600}
//contentContainerStyle={{ justifyContent: "flex-start" }}
zIndex={10}
_dark={{
bg: "gray.700",
}}
_light={{
bg: "gray.100",
}}
>
<Helmet>
<meta charSet="utf-8" />
<title>{props.title}</title>
<link rel="canonical" href="http://pandaist.com/news" />
</Helmet>
<Center py={[25, 25, 25, 50, 50]}>
<Heading
fontSize={["4xl", "6xl", "6xl"]}
_light={{
color: "black",
}}
_dark={{
color: "white",
}}
>
{props.title}
</Heading>
</Center>
<Filters hideCalendar={props.hideCalendar} />
<RecentArticles {...props} title="Recent Articles" taxon={props.taxon} />
<Box alignItems="center" justifyContent="center">
<Categories taxon={props.taxon} />
</Box>
<CategoryArticles {...props} taxon={props.taxon} />
<Box h={50} />
</ScrollView>
);
};
const mapStateToProps = (state) => {
return {
settings: state.settings,
category: state.categories,
resetdate: state.resetdate,
date: state.date,
readarticles: state.readarticles,
};
};
function mapDispatchToProps(dispatch) {
let actions = bindActionCreators({ settingsSave }, dispatch);
return { ...actions, dispatch };
}
export default connect(mapStateToProps, mapDispatchToProps)(memo(Articles));
我最大的问题是我的UI更新-当我删除:props.categoriesSaveAll(cats)
一切都是即时的,UI也是敏捷的。我已经尝试过通过useEffect钩子将数据放入本地状态来解耦UI,但这仍然不能解决问题。
由于某种原因,无论何时调用props.categoriesSaveAll(cats)
,无论在哪里调用它,都会导致这个问题。如果在它触发之前设置一个等待时间,UI按钮会立即更新(但在函数运行之前会冻结)。
如果我希望数据的初始填充基于全局存储,如何成功地将UI更新与全局存储保存分离?
EDIT:My CategoryArticles组件,在此执行实际查询:
const CategoryArticlesChild = (props) => {
const [isLoading, setIsLoading] = useState(true);
const [articleGroups, setArticleGroups] = useState([]);
const [query, setQuery] = useState(false);
const { data } = useCategoryArticlesAPIQuery(query ?? skipToken);
const loadArticlesByCategory = async () => {
const hsks = await getLevels();
let _query = {
category: props.settings.cats,
limit: 15,
ordering: "-date",
published: 1,
display_skill: hsks,
taxon: props.taxon,
};
_query = processDates(props, _query);
setQuery(_query);
};
const processMetaData = async (data) => {
if (data) {
... some data processing here, including copying data to _data.
setArticleGroups(_data);
}
};
useEffect(() => {
processMetaData(data);
}, [data]);
useEffect(() => {
loadArticlesByCategory();
}, []);
if (isLoading || articleGroups?.length === 0) {
return (
<Flex
flexGrow={1}
_dark={{
bg: "gray.700",
}}
_light={{
bg: "gray.100",
}}
alignSelf="center"
justifyContent="center"
w="100%"
minH={500}
>
<Loading />
</Flex>
);
}
return (
<FlatList
zIndex={-1}
data={articleGroups}
renderItem={(articleGroup, index) => (
<CardRow
articleGroup={articleGroup.item}
count={index}
key={index}
cardForm="WhiteCard"
/>
)}
/>
);
};
const CategoryArticles = memo(CategoryArticlesChild);
1条答案
按热度按时间qacovj5a1#
看起来你把自己绑在绳结上了!
我们来清理一下。
一般来说,状态应该存在于一个地方,而且只能存在于一个地方。这个地方看起来就在categoriesSlice reducer中。所以,让我们删除localCategories和selectedCategories,并派生selectedCategories。
通过删除所有不必要的useEffect挂钩,我们应该能够大幅减少额外渲染的数量。
更新:
让我们放回selectedCategories的本地状态,这样我们就可以“更新”组件ui,然后异步地向redux分派一个更新。
为了简化selectedCategories状态的初始化,让我们将组件一分为二,其中顶层组件负责加载数据,然后将其传递给select组件以初始化状态。