使用 array.map 后 React Native 重新渲染 UI
React Native rerendering UI after using array.map
我正在尝试让图标在单击时切换其视觉效果(如复选框)。通常在本机反应中我会做这样的事情:
const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
不过我做了一些更改,现在我似乎无法复制这种行为。我正在使用 AsyncStorage class 来存储和获取要显示的对象数组。为了简化,在下面的示例中我删除了存储代码,每个对象都有一个 'id' 和一个 'added' 属性,它本质上是复选框的布尔值。
我现在正在尝试更新每次按下时向用户显示的图标。我知道正在调用该函数,但它不会更新图标。我正在使用 array.map 创建图标列表。我在这里创建了一个演示,代码如下:https://snack.expo.dev/@figbar/array-map-icon-update
const templateObject = {
id: 0,
added: false,
};
const templateObject2 = {
id: 1,
added: true,
};
export default function App() {
const [savedNumbers, setSavedNumbers] = React.useState([]);
React.useEffect(() => {
setSavedNumbers([templateObject,templateObject2]);
}, []);
const populateSavedNumbers = () =>
savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
const updateNumber = (id) => {
let capturedIndex = -1;
for(var i = 0; i < savedNumbers.length; i += 1) {
if(savedNumbers[i].id === id) {
capturedIndex = i;
break;
}
}
let _tempArray = savedNumbers;
_tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
setSavedNumbers(_tempArray);
}
const renderPanel = (id:number, added:boolean) => {
return (
<View>
<TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
</View>
);
}
return (
<View>
<View>buttons:</View>
<View>{populateSavedNumbers()}</View>
</View>
);
}
这是一个常见的 React 陷阱,当事情看起来应该重新渲染时却没有重新渲染。 React 对新旧状态进行浅层比较,以决定是否触发重新渲染。这意味着,当将变量声明为简单地等于对象或数组的状态变量时,不会触发重新渲染,因为这两个变量现在引用相同的底层数据结构。
在这种情况下,您将 _tempArray
设置为引用数组 savedNumbers
而不是创建新数组。因此,React 的浅比较返回为“相等”,并且认为没有必要重新渲染。
要解决此问题,请更改此行:
let _tempArray = savedNumbers;
对此:
let _tempArray = [...savedNumbers];
我正在尝试让图标在单击时切换其视觉效果(如复选框)。通常在本机反应中我会做这样的事情:
const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
不过我做了一些更改,现在我似乎无法复制这种行为。我正在使用 AsyncStorage class 来存储和获取要显示的对象数组。为了简化,在下面的示例中我删除了存储代码,每个对象都有一个 'id' 和一个 'added' 属性,它本质上是复选框的布尔值。
我现在正在尝试更新每次按下时向用户显示的图标。我知道正在调用该函数,但它不会更新图标。我正在使用 array.map 创建图标列表。我在这里创建了一个演示,代码如下:https://snack.expo.dev/@figbar/array-map-icon-update
const templateObject = {
id: 0,
added: false,
};
const templateObject2 = {
id: 1,
added: true,
};
export default function App() {
const [savedNumbers, setSavedNumbers] = React.useState([]);
React.useEffect(() => {
setSavedNumbers([templateObject,templateObject2]);
}, []);
const populateSavedNumbers = () =>
savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
const updateNumber = (id) => {
let capturedIndex = -1;
for(var i = 0; i < savedNumbers.length; i += 1) {
if(savedNumbers[i].id === id) {
capturedIndex = i;
break;
}
}
let _tempArray = savedNumbers;
_tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
setSavedNumbers(_tempArray);
}
const renderPanel = (id:number, added:boolean) => {
return (
<View>
<TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
</View>
);
}
return (
<View>
<View>buttons:</View>
<View>{populateSavedNumbers()}</View>
</View>
);
}
这是一个常见的 React 陷阱,当事情看起来应该重新渲染时却没有重新渲染。 React 对新旧状态进行浅层比较,以决定是否触发重新渲染。这意味着,当将变量声明为简单地等于对象或数组的状态变量时,不会触发重新渲染,因为这两个变量现在引用相同的底层数据结构。
在这种情况下,您将 _tempArray
设置为引用数组 savedNumbers
而不是创建新数组。因此,React 的浅比较返回为“相等”,并且认为没有必要重新渲染。
要解决此问题,请更改此行:
let _tempArray = savedNumbers;
对此:
let _tempArray = [...savedNumbers];