如何根据id更新状态?
how to update state according to id?
我在每张卡片上都有一个 touchableopacity,我想将 expand 的状态设置为 true,但我想根据 id 来做,这样只有一个状态会改变,不知道如何使用 map( )?
我的代码:
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
Text,
Image,
ScrollView,
TouchableOpacity,
View,
} from 'react-native';
import axios from 'axios';
import {ROOT} from '../../../../ApiUrl';
import Icon from 'react-native-vector-icons/FontAwesome';
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState('');
let id = props.route.params.id;
const getData = () => {
let url = `some url`;
console.log('bbb');
axios
.get(url)
.then(function (res) {
console.log(res.data.content);
setData(res.data.content);
})
.catch(function (err) {
console.log(err);
});
};
useEffect(() => {
getData();
}, []);
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{padding: 10}} onPress={()=>setExpand(true)}>
{data != undefined &&
data != null &&
data.map((item) => {
return (
<View
style={{
padding: 10,
backgroundColor: '#fff',
elevation: 3,
margin: '2%',
borderRadius: 5,
}}
key={item.id}>
<View style={{alignItems: 'flex-end'}}>
<Text style={{color: 'grey', fontSize: 12}}>
{item.display_date}
</Text>
</View>
<View style={{flexDirection: 'row'}}>
<View>
<Image
source={require('../../assets/atbirth.jpg')}
style={{height: 40, width: 50}}
resizeMode="contain"
/>
</View>
<View style={{flex: 1}}>
<View style={{flexDirection: 'row', flex: 1}}>
<Text
key={item.id}
style={{
fontFamily: 'Roboto',
fontSize: 18,
fontWeight: 'bold',
}}>
{item.name}
</Text>
</View>
<View style={{flexDirection: 'row', width: '30%'}}>
{item.vaccine_list.map((i) => {
return (
<View style={{flexDirection: 'row'}}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{fontFamily: 'Roboto', fontSize: 15}}>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{alignItems: 'flex-end', marginTop: '1%'}}>
<View style={{flexDirection: 'row'}}>
<Text
style={{
color: 'red',
fontSize: 14,
fontWeight: 'bold',
}}>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{marginTop: '1%', marginLeft: '1%'}}
/>
</View>
</View>
</View>
);
})}
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
}
任何建议都很好,如果还需要其他任何东西以更好地理解,请让 mw 知道
我还没有测试代码是否正常工作,但您可以尝试类似的方法。您可以为项目创建单独的组件并为每个项目设置状态。
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState("");
const VaccinationListItem = (item) => {
const [expand, setExpand] = useState(false);
return (
<TouchableOpacity style={{ padding: 10 }} onPress={() => setExpand(true)}>
<View
style={{
padding: 10,
backgroundColor: "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5,
}}
key={item.id}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
source={require("../../assets/atbirth.jpg")}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold",
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((i) => {
return (
<View style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold",
}}
>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{ marginTop: "1%", marginLeft: "1%" }}
/>
</View>
</View>
</View>
</TouchableOpacity>
);
};
return (
<SafeAreaView>
<ScrollView>
{data != undefined &&
data != null &&
data.map((item) => {
VaccinationListItem(item);
})}
</ScrollView>
</SafeAreaView>
);
}
通常,如果您想切换任何单个元素,您应该将其 id 存储在 expand
中(而不是布尔值),并在呈现数组时简单地检查是否有任何特定元素的 id 匹配,即 element.id === expand
.当触摸任何新元素时,将其 id 弹出到那里,如果 id 已经存在,则设置为 null
以折叠。
export default function VaccinationListScreen(props) {
const [expandId, setExpandId] = useState(null); // <-- stores null or id, initially null
...
// Create curried handler to set/toggle expand id
const expandHandler = (id) => () =>
setExpandId((oldId) => (oldId === id ? null : id));
return (
<SafeAreaView>
<ScrollView>
{data &&
data.map((item) => {
return (
<View
...
key={item.id}
>
<TouchableOpacity
style={{ padding: 10 }}
onPress={expandHandler(item.id)} // <-- attach callback and pass id
>
...
</TouchableOpacity>
{item.id === expandId && ( // <-- check if id match expand id
<ExpandComponent />
)}
</View>
);
})}
</ScrollView>
</SafeAreaView>
);
}
当我查看您的代码时,<TouchableOpacity>
一次包装您所有的卡片,而不是每套卡片。如果您以这种方式实现您的代码(如果不是不可能的话),那么您将很难引用每个卡片 ID 并根据卡片 ID 将扩展状态设置为 true。
我的建议是将 <TouchableOpacity>
包含到 map()
函数嵌套中,以便于引用每个卡片函数。
我重现了这个特定问题并实施了一个解决方案,在该解决方案中,我能够根据每个卡片 ID 将展开状态设置为 true。
您可以单击沙盒 link 观看演示。
https://codesandbox.io/s/accordingtoid-4px1w
沙盒中的代码:
import React, { useState, useEffect } from "react";
import {
SafeAreaView,
Text,
Image,
TouchableOpacity,
View
} from "react-native";
// import axios from 'axios';
// import {ROOT} from '../../../../ApiUrl';
// import Icon from "react-native-vector-icons/FontAwesome";
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState({});
const [data, setData] = useState([]);
// let id = props.route.params.id;
// const getData = () => {
// let url = `some url`;
// console.log('bbb');
// axios
// .get(url)
// .then(function (res) {
// console.log(res.data.content);
// setData(res.data.content);
// })
// .catch(function (err) {
// console.log(err);
// });
// };
// useEffect(() => {
// getData();
// }, []);
useEffect(() => {
// In order to simulate and reproduce the problem
// Assume that these are the data that you fetch from an API
const dataContent = [
{
id: 1,
name: "At Birth",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "BCG" },
{ name: "Hepatitis B" },
{ name: "OPV 0" }
],
child_vacc_status: { text: "Missed" }
},
{
id: 2,
name: "At 6 Weeks",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "IPV" },
{ name: "PCV" },
{ name: "Hepatitis b" },
{ name: "DTP" },
{ name: "HiB" },
{ name: "Rotavirus" }
],
child_vacc_status: { text: "Missed" }
}
];
setData(dataContent);
}, []);
function handleOnPress(id) {
setExpand((prev) => {
let toggleId;
if (prev[id]) {
toggleId = { [id]: false };
} else {
toggleId = { [id]: true };
}
return { ...toggleId };
});
}
useEffect(() => {
console.log(expand); // check console to see the value
}, [expand]);
return (
<SafeAreaView>
{data !== undefined &&
data !== null &&
data.map((item) => {
return (
<TouchableOpacity
key={item.id}
style={{
padding: 10
}}
onPress={() => handleOnPress(item.id)}
>
<View
style={{
padding: 10,
backgroundColor: expand[item.id] ? "lightgrey" : "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5
}}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
// source={require('../../assets/atbirth.jpg')}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold"
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((item, i) => {
return (
<View key={i} style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{item.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold"
}}
>
{item.child_vacc_status.text}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
})}
</SafeAreaView>
);
}
我在每张卡片上都有一个 touchableopacity,我想将 expand 的状态设置为 true,但我想根据 id 来做,这样只有一个状态会改变,不知道如何使用 map( )?
我的代码:
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
Text,
Image,
ScrollView,
TouchableOpacity,
View,
} from 'react-native';
import axios from 'axios';
import {ROOT} from '../../../../ApiUrl';
import Icon from 'react-native-vector-icons/FontAwesome';
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState('');
let id = props.route.params.id;
const getData = () => {
let url = `some url`;
console.log('bbb');
axios
.get(url)
.then(function (res) {
console.log(res.data.content);
setData(res.data.content);
})
.catch(function (err) {
console.log(err);
});
};
useEffect(() => {
getData();
}, []);
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{padding: 10}} onPress={()=>setExpand(true)}>
{data != undefined &&
data != null &&
data.map((item) => {
return (
<View
style={{
padding: 10,
backgroundColor: '#fff',
elevation: 3,
margin: '2%',
borderRadius: 5,
}}
key={item.id}>
<View style={{alignItems: 'flex-end'}}>
<Text style={{color: 'grey', fontSize: 12}}>
{item.display_date}
</Text>
</View>
<View style={{flexDirection: 'row'}}>
<View>
<Image
source={require('../../assets/atbirth.jpg')}
style={{height: 40, width: 50}}
resizeMode="contain"
/>
</View>
<View style={{flex: 1}}>
<View style={{flexDirection: 'row', flex: 1}}>
<Text
key={item.id}
style={{
fontFamily: 'Roboto',
fontSize: 18,
fontWeight: 'bold',
}}>
{item.name}
</Text>
</View>
<View style={{flexDirection: 'row', width: '30%'}}>
{item.vaccine_list.map((i) => {
return (
<View style={{flexDirection: 'row'}}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{fontFamily: 'Roboto', fontSize: 15}}>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{alignItems: 'flex-end', marginTop: '1%'}}>
<View style={{flexDirection: 'row'}}>
<Text
style={{
color: 'red',
fontSize: 14,
fontWeight: 'bold',
}}>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{marginTop: '1%', marginLeft: '1%'}}
/>
</View>
</View>
</View>
);
})}
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
}
任何建议都很好,如果还需要其他任何东西以更好地理解,请让 mw 知道
我还没有测试代码是否正常工作,但您可以尝试类似的方法。您可以为项目创建单独的组件并为每个项目设置状态。
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState("");
const VaccinationListItem = (item) => {
const [expand, setExpand] = useState(false);
return (
<TouchableOpacity style={{ padding: 10 }} onPress={() => setExpand(true)}>
<View
style={{
padding: 10,
backgroundColor: "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5,
}}
key={item.id}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
source={require("../../assets/atbirth.jpg")}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold",
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((i) => {
return (
<View style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold",
}}
>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{ marginTop: "1%", marginLeft: "1%" }}
/>
</View>
</View>
</View>
</TouchableOpacity>
);
};
return (
<SafeAreaView>
<ScrollView>
{data != undefined &&
data != null &&
data.map((item) => {
VaccinationListItem(item);
})}
</ScrollView>
</SafeAreaView>
);
}
通常,如果您想切换任何单个元素,您应该将其 id 存储在 expand
中(而不是布尔值),并在呈现数组时简单地检查是否有任何特定元素的 id 匹配,即 element.id === expand
.当触摸任何新元素时,将其 id 弹出到那里,如果 id 已经存在,则设置为 null
以折叠。
export default function VaccinationListScreen(props) {
const [expandId, setExpandId] = useState(null); // <-- stores null or id, initially null
...
// Create curried handler to set/toggle expand id
const expandHandler = (id) => () =>
setExpandId((oldId) => (oldId === id ? null : id));
return (
<SafeAreaView>
<ScrollView>
{data &&
data.map((item) => {
return (
<View
...
key={item.id}
>
<TouchableOpacity
style={{ padding: 10 }}
onPress={expandHandler(item.id)} // <-- attach callback and pass id
>
...
</TouchableOpacity>
{item.id === expandId && ( // <-- check if id match expand id
<ExpandComponent />
)}
</View>
);
})}
</ScrollView>
</SafeAreaView>
);
}
当我查看您的代码时,<TouchableOpacity>
一次包装您所有的卡片,而不是每套卡片。如果您以这种方式实现您的代码(如果不是不可能的话),那么您将很难引用每个卡片 ID 并根据卡片 ID 将扩展状态设置为 true。
我的建议是将 <TouchableOpacity>
包含到 map()
函数嵌套中,以便于引用每个卡片函数。
我重现了这个特定问题并实施了一个解决方案,在该解决方案中,我能够根据每个卡片 ID 将展开状态设置为 true。
您可以单击沙盒 link 观看演示。
https://codesandbox.io/s/accordingtoid-4px1w
沙盒中的代码:
import React, { useState, useEffect } from "react";
import {
SafeAreaView,
Text,
Image,
TouchableOpacity,
View
} from "react-native";
// import axios from 'axios';
// import {ROOT} from '../../../../ApiUrl';
// import Icon from "react-native-vector-icons/FontAwesome";
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState({});
const [data, setData] = useState([]);
// let id = props.route.params.id;
// const getData = () => {
// let url = `some url`;
// console.log('bbb');
// axios
// .get(url)
// .then(function (res) {
// console.log(res.data.content);
// setData(res.data.content);
// })
// .catch(function (err) {
// console.log(err);
// });
// };
// useEffect(() => {
// getData();
// }, []);
useEffect(() => {
// In order to simulate and reproduce the problem
// Assume that these are the data that you fetch from an API
const dataContent = [
{
id: 1,
name: "At Birth",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "BCG" },
{ name: "Hepatitis B" },
{ name: "OPV 0" }
],
child_vacc_status: { text: "Missed" }
},
{
id: 2,
name: "At 6 Weeks",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "IPV" },
{ name: "PCV" },
{ name: "Hepatitis b" },
{ name: "DTP" },
{ name: "HiB" },
{ name: "Rotavirus" }
],
child_vacc_status: { text: "Missed" }
}
];
setData(dataContent);
}, []);
function handleOnPress(id) {
setExpand((prev) => {
let toggleId;
if (prev[id]) {
toggleId = { [id]: false };
} else {
toggleId = { [id]: true };
}
return { ...toggleId };
});
}
useEffect(() => {
console.log(expand); // check console to see the value
}, [expand]);
return (
<SafeAreaView>
{data !== undefined &&
data !== null &&
data.map((item) => {
return (
<TouchableOpacity
key={item.id}
style={{
padding: 10
}}
onPress={() => handleOnPress(item.id)}
>
<View
style={{
padding: 10,
backgroundColor: expand[item.id] ? "lightgrey" : "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5
}}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
// source={require('../../assets/atbirth.jpg')}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold"
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((item, i) => {
return (
<View key={i} style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{item.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold"
}}
>
{item.child_vacc_status.text}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
})}
</SafeAreaView>
);
}