- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
见 Reproducible demo或 code .
我尝试在 react-native FlatList 上构建一个简单的可选列表。功能很简单:FlatList 中的每一行都是可选择的。如果某行还没有被选中,点击该行就会选中它;如果该行已被选中,单击该行将取消选中它。
我面临的问题是每次单击一行时,所有行都会重新渲染,这可以从日志中得知(例如“渲染项 id=cameron.nguyen@example.com,selected=false”)。我想避免重新渲染未更改的行,因为重新渲染可能很昂贵(在我想加载非常大的图像或列表很长的情况下),但不知道如何做。我都试过了 <MomoizedItem />
和 <MemoizedItem2 />
通过杠杆React.memo
,但前者根本不会改变重新渲染的行为,而后者使应用程序的行为非常怪异,您可以尝试更换 <Item />
用其中之一来看看效果。我也尝试使用 onClickCallBack
在 onClick
,但这也无济于事。
我在用 React.memo
或 React.useCallBack
不正确?我可以做些什么来满足需求?谢谢你。
如果代码链接过期,请粘贴以下代码:
import React, { memo, useEffect, useState } from "react";
import { SafeAreaView, FlatList, StyleSheet } from "react-native";
import Constants from "expo-constants";
import { Set } from "immutable";
import { Button, ListItem } from "react-native-elements";
import axios from "axios";
const Item = ({ id, title, avatarUrl, selected, onClick }) => {
console.log(`rendering item id=${id}, selected=${selected}`);
return (
<ListItem
title={title}
leftAvatar={{ source: { uri: avatarUrl } }}
containerStyle={[
styles.item,
{ backgroundColor: selected ? "#6e3b6e" : "#f9c2ff" }
]}
underlayColor="transparent"
onPress={() => onClick(id)}
/>
);
};
function itemEq(prevItem, nextItem) {
return prevItem.id === nextItem.id && prevItem.selected === nextItem.selected;
}
// Does not make a difference, every time a row is clicked, all rows are re-rendered
const MemoizedItem = memo(Item);
// Make some difference but the behavior looks very weird. Try click around and see the log
const MemoizedItem2 = memo(Item, itemEq);
const Items = ({ data, selectedItems, onClick }) => {
console.log("rendering items");
// Replace <Item /> with <MemoizedItem /> or <MemoizedItem2 /> to see effect
const _renderItem = ({ item }) => (
<Item
id={item.email}
title={`${item.name.title} ${item.name.first} ${item.name.last}`}
avatarUrl={item.picture.thumbnail}
selected={selectedItems.has(item.email)}
onClick={onClick}
/>
);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={item => item.email}
extraData={selectedItems}
/>
);
};
const App = () => {
const [items, setItems] = useState([]);
const [selectedItems, setSelectedItems] = useState(Set());
useEffect(() => {
const fetchData = async () => {
console.log("fetching data");
// Read 5 random users back
// Each user is like this:
// {
// "gender":"male",
// "name":{
// "title":"Mr",
// "first":"Harley",
// "last":"Zhang"
// },
// "location":{
// "street":{
// "number":6470,
// "name":"Buckleys Road"
// },
// "city":"Palmerston North",
// "state":"Manawatu-Wanganui",
// "country":"New Zealand",
// "postcode":90911,
// "coordinates":{
// "latitude":"66.2907",
// "longitude":"-18.0881"
// },
// "timezone":{
// "offset":"+8:00",
// "description":"Beijing, Perth, Singapore, Hong Kong"
// }
// },
// "email":"harley.zhang@example.com",
// "login":{
// "uuid":"6fda195e-3e63-476c-84d0-7c577c7b74f9",
// "username":"smallbear541",
// "password":"daisy1",
// "salt":"p6AmByUq",
// "md5":"0358f2385a9936369adc89b9233f037b",
// "sha1":"8decc817cf32ca6e58814502bb3e54152208c5b5",
// "sha256":"96ff7627348250646edd31238504271840a0cb6aaac293782f7eec1a6f884c07"
// },
// "dob":{
// "date":"1987-12-07T13:00:15.244Z",
// "age":33
// },
// "registered":{
// "date":"2008-01-23T19:33:01.672Z",
// "age":12
// },
// "phone":"(474)-743-9612",
// "cell":"(539)-021-1315",
// "id":{
// "name":"",
// "value":null
// },
// "picture":{
// "large":"https://randomuser.me/api/portraits/men/49.jpg",
// "medium":"https://randomuser.me/api/portraits/med/men/49.jpg",
// "thumbnail":"https://randomuser.me/api/portraits/thumb/men/49.jpg"
// },
// "nat":"NZ"
// }
const results = await axios("https://randomuser.me/api/?results=5");
setItems(results.data.results);
};
fetchData();
}, []);
const onClick = id => {
const newSelectedItems = selectedItems.has(id)
? selectedItems.delete(id)
: selectedItems.add(id);
console.log(`selected items=${JSON.stringify(selectedItems, null, 2)}`);
console.log(
`new selected items=${JSON.stringify(newSelectedItems, null, 2)}`
);
setSelectedItems(newSelectedItems);
}
// Does not help
const onClickUseCallBack = React.useCallback(
id => {
const newSelectedItems = selectedItems.has(id)
? selectedItems.delete(id)
: selectedItems.add(id);
console.log(`selected items=${JSON.stringify(selectedItems, null, 2)}`);
console.log(
`new selected items=${JSON.stringify(newSelectedItems, null, 2)}`
);
setSelectedItems(newSelectedItems);
},
[selectedItems]
);
return (
<SafeAreaView style={styles.container}>
<Items data={items} selectedItems={selectedItems} onClick={onClick} />
<Button
title="Print"
onPress={() => console.log(`Printing selected items ${selectedItems}`)}
/>
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: Constants.statusBarHeight,
marginHorizontal: 16
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8
}
});
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"axios": "^0.19.2",
"expo": "~36.0.0",
"immutable": "^4.0.0-rc.12",
"react": "~16.9.0",
"react-dom": "~16.9.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
"react-native-elements": "^1.2.7",
"react-native-web": "~0.11.7"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"babel-preset-expo": "~8.0.0"
},
"private": true
}
最佳答案
MomoizedItem
+ onClickUseCallBack
是一个好的开始。
重新渲染发生的原因是 onClickUseCallBack
被执行。看,你有 selectedItems
作为 useCallback
的第二个参数, 每次选择/取消选择一个项目时,selectedItems
更改,导致新的 onClickUseCallBack
被创建然后传递给每个项目,这打破了 memo
并导致每个项目重新渲染。
要解决此问题,您需要删除 selectedItems
来自 useCallback
的第二个参数,然后为了避免具有陈旧的状态值(由于闭包的工作方式),请使用状态 setter 的函数形式来获得新值。
const onClickUseCallBack = React.useCallback(
id => {
setSelectedItems((selectedItems) => {
const newSelectedItems = selectedItems.has(id)
? selectedItems.delete(id)
: selectedItems.add(id);
return newSelectedItems
});
},
[]
);
import React, { memo, useEffect, useState } from "react";
import { SafeAreaView, FlatList, StyleSheet } from "react-native";
import Constants from "expo-constants";
import { Set } from "immutable";
import { Button, ListItem } from "react-native-elements";
import axios from "axios";
const Item = ({ id, title, avatarUrl, selected, onClick }) => {
console.log(`rendering item id=${id}, selected=${selected}`);
return (
<ListItem
title={title}
leftAvatar={{ source: { uri: avatarUrl } }}
containerStyle={[
styles.item,
{ backgroundColor: selected ? "#6e3b6e" : "#f9c2ff" }
]}
underlayColor="transparent"
onPress={() => onClick(id)}
/>
);
};
function itemEq(prevItem, nextItem) {
return prevItem.id === nextItem.id && prevItem.selected === nextItem.selected;
}
// Does not make a difference, every time a row is clicked, all rows are re-rendered
const MemoizedItem = memo(Item);
// Make some difference but the behavior looks very weird. Try click around and see the log
const MemoizedItem2 = memo(Item, itemEq);
const Items = ({ data, selectedItems, onClick }) => {
console.log("rendering items");
// Replace <Item /> with <MemoizedItem /> or <MemoizedItem2 /> to see effect
const _renderItem = ({ item }) => (
<MemoizedItem
id={item.email}
title={`${item.name.title} ${item.name.first} ${item.name.last}`}
avatarUrl={item.picture.thumbnail}
selected={selectedItems.has(item.email)}
onClick={onClick}
/>
);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={item => item.email}
extraData={selectedItems}
/>
);
};
const App = () => {
const [items, setItems] = useState([]);
const [selectedItems, setSelectedItems] = useState(Set());
useEffect(() => {
const fetchData = async () => {
console.log("fetching data");
// Read 5 random users back
// Each user is like this:
// {
// "gender":"male",
// "name":{
// "title":"Mr",
// "first":"Harley",
// "last":"Zhang"
// },
// "location":{
// "street":{
// "number":6470,
// "name":"Buckleys Road"
// },
// "city":"Palmerston North",
// "state":"Manawatu-Wanganui",
// "country":"New Zealand",
// "postcode":90911,
// "coordinates":{
// "latitude":"66.2907",
// "longitude":"-18.0881"
// },
// "timezone":{
// "offset":"+8:00",
// "description":"Beijing, Perth, Singapore, Hong Kong"
// }
// },
// "email":"harley.zhang@example.com",
// "login":{
// "uuid":"6fda195e-3e63-476c-84d0-7c577c7b74f9",
// "username":"smallbear541",
// "password":"daisy1",
// "salt":"p6AmByUq",
// "md5":"0358f2385a9936369adc89b9233f037b",
// "sha1":"8decc817cf32ca6e58814502bb3e54152208c5b5",
// "sha256":"96ff7627348250646edd31238504271840a0cb6aaac293782f7eec1a6f884c07"
// },
// "dob":{
// "date":"1987-12-07T13:00:15.244Z",
// "age":33
// },
// "registered":{
// "date":"2008-01-23T19:33:01.672Z",
// "age":12
// },
// "phone":"(474)-743-9612",
// "cell":"(539)-021-1315",
// "id":{
// "name":"",
// "value":null
// },
// "picture":{
// "large":"https://randomuser.me/api/portraits/men/49.jpg",
// "medium":"https://randomuser.me/api/portraits/med/men/49.jpg",
// "thumbnail":"https://randomuser.me/api/portraits/thumb/men/49.jpg"
// },
// "nat":"NZ"
// }
const results = await axios("https://randomuser.me/api/?results=5");
setItems(results.data.results);
};
fetchData();
}, []);
// Does not help
const onClickUseCallBack = React.useCallback(
id => {
setSelectedItems((selectedItems) => {
const newSelectedItems = selectedItems.has(id)
? selectedItems.delete(id)
: selectedItems.add(id);
return newSelectedItems
});
},
[]
);
return (
<SafeAreaView style={styles.container}>
<Items data={items} selectedItems={selectedItems} onClick={onClickUseCallBack} />
<Button
title="Print"
onPress={() => console.log(`Printing selected items ${selectedItems}`)}
/>
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: Constants.statusBarHeight,
marginHorizontal: 16
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8
}
});
关于reactjs - 每次选择/取消选择一行时,如何避免 react 原生 FlatList 重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60356434/
问题是,当用户回复彼此的帖子时,我必须这样做: margin-left:40px; 对于 1 级深度 react margin-left:80px; 对于 2 层深等 但是我想让 react div
我试图弄清楚如何将 React Router 与 React VR 连接起来。 首先,我应该使用 react-router dom/native ?目前尚不清楚,因为 React VR 构建在 Rea
我是 React 或一般编码背景的新手。我不确定这些陈述之间有什么区别 import * as react from 'react' 和 import react from 'react' 提前致谢!
我正在使用最新的稳定版本的 react、react-native、react-test-renderer、react-dom。 然而,react-native 依赖于 react@16.0.0-alp
是否 react 原生 应用程序开发可以通过软件架构实现,例如 MVC、MVP、MVVM ? 谢谢你。 最佳答案 是的。 React Native 只是你提到的那些软件设计模式中的“V”。如果你考虑其
您好我正在尝试在我的导航器右按钮中绑定(bind)一个功能, 但它给出了错误。 这是我的代码: import React, { Component } from 'react'; import Ico
我使用react native创建了一个应用程序,我正在尝试生成apk。在http://facebook.github.io/react-native/docs/signed-apk-android.
1 [我尝试将分页的 z-index 更改为 0,但没有成功] 这是我的codesandbox的链接:请检查最后一个选择下拉列表,它位于分页后面。 https://codesandbox.io/s/j
我注意到 React 可以这样导入: import * as React from 'react'; ...或者像这样: import React from 'react'; 第一个导入 react
我是 react-native 的新手。我正在使用 React Native Paper 为所有屏幕提供主题。我也在使用 react 导航堆栈导航器和抽屉导航器。首先,对于导航,论文主题在导航组件中不
我有一个使用 Ignite CLI 创建的 React Native 应用程序.我正在尝试将 TabNavigator 与 React Navigation 结合使用,但我似乎无法弄清楚如何将数据从一
我正在尝试在我的 React 应用程序中进行快照测试。我已经在使用 react-testing-library 进行一般的单元测试。然而,对于快照测试,我在网上看到了不同的方法,要么使用 react-
我正在使用 react-native 构建跨平台 native 应用程序,并使用 react-navigation 在屏幕之间导航和使用 redux 管理导航状态。当我嵌套导航器时会出现问题。 例如,
由于分页和 React Native Navigation,我面临着一种复杂的问题。 单击具有类别列表的抽屉,它们都将转到屏幕 问题陈述: 当我随机点击类别时,一切正常。但是,在分页过程中遇到问题。假
这是我的抽屉导航: const DashboardStack = StackNavigator({ Dashboard: { screen: Dashboard
尝试构建 react-native android 应用程序但出现以下错误 info Running jetifier to migrate libraries to AndroidX. You ca
我目前正在一个应用程序中实现 React Router v.4,我也在其中使用 Webpack 进行捆绑。在我的 webpack 配置中,我将 React、ReactDOM 和 React-route
我正在使用 React.children 渲染一些带有 react router 的子路由(对于某个主路由下的所有子路由。 这对我来说一直很好,但是我之前正在解构传递给 children 的 Prop
当我运行 React 应用程序时,它显示 export 'React'(导入为 'React')在 'react' 中找不到。所有页面错误 see image here . 最佳答案 根据图像中的错误
当我使用这个例子在我的应用程序上实现 Image-slider 时,我遇到了这个错误。 import React,{Component} from 'react' import {View,T
我是一名优秀的程序员,十分优秀!