根据 API fetch 渲染新的 React 组件
Render new react component based on API fetch
我正在尝试创建一个 React 应用程序,它将根据 API 响应呈现组件(获取主题标题、描述以及属于它们的课程的相同内容)。目的是能够向数据库添加新的主题和课程,而不必在前端添加任何代码来呈现它们。
import { Swiper, SwiperSlide } from 'swiper/react';
import Topic from './Topic.js';
import 'swiper/swiper-bundle.min.css'
import 'swiper/swiper.min.css'
function Swiper() {
return (
<Swiper
spaceBetween={50}
slidesPerView={1}
loop={true}
direction={'horizontal'}
>
<SwiperSlide><Topic /></SwiperSlide>
</Swiper>
);
};
export default Swiper;
我有这个 Swiper 组件,我将在其中获取所有主题的列表,并根据响应用幻灯片填充滑动器。
我遇到问题的地方是,对于每个主题,在点击事件上它需要:
打开一个新页面并发出第二个 API 请求列出该主题中的所有课程以填充该页面。然而,这将需要我为每个主题手动创建一个新页面,并且我正在尝试尽量减少在前端更新代码的必要性。
或者我可以将 swiper 组件换成显示课程的组件。但是,这种方法我需要发出第二个 api 请求,然后将该数据传递给主应用程序组件,以便它可以用新组件替换 Swiper。我不知道该怎么做。
对于我如何实现这一点的任何想法,或者我对如何做到这一点的假设是否准确,我将不胜感激。
谢谢。
Would be very grateful for any ideas on how I could achieve this, or if my assumptions on how to do this are accurate.
所以这可能不是实现您想要的东西的最佳方式,但这是我的处理方式...
open a new page and make a second API request listing all courses in that topic to populate that page. However this would require me to make a new page for each Topic manually and I'm trying to minimize having to update code on the front end.
我会用动态路由来做到这一点,并使用像 react-router 这样的东西来添加一个 link,如下所示:
<Link to={`/topic/${topic.name`}>
{topic.name}
</Link>
然后在我的应用程序中使用如下内容捕获该路由:
<Route path="/topic/:topicName" element={<Topic />}/>
现在我需要创建我的主题页面,并确保在我们在该组件中呈现任何内容之前获取有关该主题的更多详细信息,就像我在下面给出的内容可能会给您一个想法。另请查看如何使用 useParams
将 topicName 从 url 路径中拉出:
const Topic = () => {
const [loading, setLoading] = useState(true);
const { topicName } = useParams();
const [topic, setTopic] = useState(null);
useEffect(() => {
/**
Add API Code here to fetch the topic, and when you get a response call
setTopic(response.data) or something similar to set it locally.
Also make sure you call setLoading(false);
*/
}, []);
if(loading){
return (
<h1>Loading Topic...</h1>
)
}
if(topic){
return (
<div>
<h1>{topic.name}</h1>
</div>
);
}
return null;
}
希望这能让您了解如何做这样的事情,如果您需要更多说明,请告诉我。
我也有像你一样的东西,Swiper 要求为每个对象提供唯一的“密钥”,但我很确定我从地图 API 收集的博物馆每个都应该有一个 UID
import { View, Text, Button, TouchableOpacity, Image } from "react-native";
import React, { useLayoutEffect, useRef, useState, useEffect } from "react";
import { useNavigation } from "@react-navigation/native";
import useAuth from "../hooks/useAuth";
import { SafeAreaView } from "react-native-safe-area-context";
import { useTailwind } from "tailwind-rn";
import { Ionicons } from "@expo/vector-icons";
import Swiper from "react-native-deck-swiper";
import * as Location from "expo-location";
import * as Permissions from "expo-permissions";
import { serverTimestamp, setDoc, doc } from "firebase/firestore";
import { GOOGLE_MAPS_APIKEY } from "@env";
const HomeScreen = () => {
const navigation = useNavigation();
const { user, logout } = useAuth();
const tailwind = useTailwind();
const swipeRef = useRef(null);
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [data, setData] = useState([]);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
setErrorMsg("Permission to access location was denied");
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
const url =
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?";
const location2 = `location=${location.coords.latitude},${location.coords.longitude}`;
const radius = "&radius=2000";
const type = "&keyword=museum";
const key = "&key=GOOGLE_MAPS_APIKEY";
const restaurantSearchUrl = url + location2 + radius + type + key;
const fetchData = async () => {
try {
const response = await fetch(restaurantSearchUrl);
const json = await response.json();
console.log(json.results);
setData(json.results);
} catch (error) {
console.log("error", error);
}
};
fetchData();
/* catch the error here */
})();
}, []);
return (
<SafeAreaView style={tailwind("flex-1")}>
{/*Header */}
<View style={tailwind("flex-row items-center justify-between px-5")}>
<TouchableOpacity onPress={logout}>
<Image
style={tailwind("h-10 w-10 rounded-full")}
source={{ uri: user.photoURL }}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("Modal")}>
<Image
style={tailwind("h-14 w-14")}
source={require("../logo.png")}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("DummyHome")}>
<Ionicons name="chatbubbles-sharp" size={30} />
</TouchableOpacity>
</View>
{/*End of Header */}
{/*Card */}
<View style={tailwind("flex-1 mt-0.5")}>
<Swiper
ref={swipeRef}
containerStyle={{ backgroundColor: "transparent" }}
cards={DUMMY_DATA}
stackSize={5}
cardIndex={0}
verticalSwipe={false}
animateCardOpacity
onSwipedLeft={() => {
console.log("Nay");
}}
onSwipedRight={() => {
console.log("Yay");
}}
overlayLabels={{
left: {
title: "NAY",
style: {
label: {
textAlign: "right",
color: "red", //make it purple
},
},
},
right: {
title: "YAY",
style: {
label: {
color: "purple",
},
},
},
}}
renderCard={(card) =>
card ? (
<View
key={card.id}
style={tailwind("relative bg-white-500 h-3/4 rounded-2xl")}
>
<Image
style={tailwind("absolute top-0 h-full w-full rounded-xl")}
source={{ uri: card.photoURL }}
/>
<View
style={tailwind(
"absolute bottom-0 bg-white w-full flex-row justify-between items-between h-20 px-6 py-2 rounded-b-xl"
)}
>
<View>
<Text style={tailwind("text-xl font-bold")}>
{card.name}
</Text>
</View>
<Text style={tailwind("text-2xl font-bold")}>
{card.address}
</Text>
</View>
</View>
) : (
<Text>ENDDDDddd</Text>
)
}
/>
</View>
<Button title="Places" onPress={() => this.handleRestaurantSearch()} />
</SafeAreaView>
);
};
export default HomeScreen;
我正在尝试创建一个 React 应用程序,它将根据 API 响应呈现组件(获取主题标题、描述以及属于它们的课程的相同内容)。目的是能够向数据库添加新的主题和课程,而不必在前端添加任何代码来呈现它们。
import { Swiper, SwiperSlide } from 'swiper/react';
import Topic from './Topic.js';
import 'swiper/swiper-bundle.min.css'
import 'swiper/swiper.min.css'
function Swiper() {
return (
<Swiper
spaceBetween={50}
slidesPerView={1}
loop={true}
direction={'horizontal'}
>
<SwiperSlide><Topic /></SwiperSlide>
</Swiper>
);
};
export default Swiper;
我有这个 Swiper 组件,我将在其中获取所有主题的列表,并根据响应用幻灯片填充滑动器。
我遇到问题的地方是,对于每个主题,在点击事件上它需要:
打开一个新页面并发出第二个 API 请求列出该主题中的所有课程以填充该页面。然而,这将需要我为每个主题手动创建一个新页面,并且我正在尝试尽量减少在前端更新代码的必要性。
或者我可以将 swiper 组件换成显示课程的组件。但是,这种方法我需要发出第二个 api 请求,然后将该数据传递给主应用程序组件,以便它可以用新组件替换 Swiper。我不知道该怎么做。
对于我如何实现这一点的任何想法,或者我对如何做到这一点的假设是否准确,我将不胜感激。
谢谢。
Would be very grateful for any ideas on how I could achieve this, or if my assumptions on how to do this are accurate.
所以这可能不是实现您想要的东西的最佳方式,但这是我的处理方式...
open a new page and make a second API request listing all courses in that topic to populate that page. However this would require me to make a new page for each Topic manually and I'm trying to minimize having to update code on the front end.
我会用动态路由来做到这一点,并使用像 react-router 这样的东西来添加一个 link,如下所示:
<Link to={`/topic/${topic.name`}>
{topic.name}
</Link>
然后在我的应用程序中使用如下内容捕获该路由:
<Route path="/topic/:topicName" element={<Topic />}/>
现在我需要创建我的主题页面,并确保在我们在该组件中呈现任何内容之前获取有关该主题的更多详细信息,就像我在下面给出的内容可能会给您一个想法。另请查看如何使用 useParams
将 topicName 从 url 路径中拉出:
const Topic = () => {
const [loading, setLoading] = useState(true);
const { topicName } = useParams();
const [topic, setTopic] = useState(null);
useEffect(() => {
/**
Add API Code here to fetch the topic, and when you get a response call
setTopic(response.data) or something similar to set it locally.
Also make sure you call setLoading(false);
*/
}, []);
if(loading){
return (
<h1>Loading Topic...</h1>
)
}
if(topic){
return (
<div>
<h1>{topic.name}</h1>
</div>
);
}
return null;
}
希望这能让您了解如何做这样的事情,如果您需要更多说明,请告诉我。
我也有像你一样的东西,Swiper 要求为每个对象提供唯一的“密钥”,但我很确定我从地图 API 收集的博物馆每个都应该有一个 UID
import { View, Text, Button, TouchableOpacity, Image } from "react-native";
import React, { useLayoutEffect, useRef, useState, useEffect } from "react";
import { useNavigation } from "@react-navigation/native";
import useAuth from "../hooks/useAuth";
import { SafeAreaView } from "react-native-safe-area-context";
import { useTailwind } from "tailwind-rn";
import { Ionicons } from "@expo/vector-icons";
import Swiper from "react-native-deck-swiper";
import * as Location from "expo-location";
import * as Permissions from "expo-permissions";
import { serverTimestamp, setDoc, doc } from "firebase/firestore";
import { GOOGLE_MAPS_APIKEY } from "@env";
const HomeScreen = () => {
const navigation = useNavigation();
const { user, logout } = useAuth();
const tailwind = useTailwind();
const swipeRef = useRef(null);
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [data, setData] = useState([]);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
setErrorMsg("Permission to access location was denied");
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
const url =
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?";
const location2 = `location=${location.coords.latitude},${location.coords.longitude}`;
const radius = "&radius=2000";
const type = "&keyword=museum";
const key = "&key=GOOGLE_MAPS_APIKEY";
const restaurantSearchUrl = url + location2 + radius + type + key;
const fetchData = async () => {
try {
const response = await fetch(restaurantSearchUrl);
const json = await response.json();
console.log(json.results);
setData(json.results);
} catch (error) {
console.log("error", error);
}
};
fetchData();
/* catch the error here */
})();
}, []);
return (
<SafeAreaView style={tailwind("flex-1")}>
{/*Header */}
<View style={tailwind("flex-row items-center justify-between px-5")}>
<TouchableOpacity onPress={logout}>
<Image
style={tailwind("h-10 w-10 rounded-full")}
source={{ uri: user.photoURL }}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("Modal")}>
<Image
style={tailwind("h-14 w-14")}
source={require("../logo.png")}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("DummyHome")}>
<Ionicons name="chatbubbles-sharp" size={30} />
</TouchableOpacity>
</View>
{/*End of Header */}
{/*Card */}
<View style={tailwind("flex-1 mt-0.5")}>
<Swiper
ref={swipeRef}
containerStyle={{ backgroundColor: "transparent" }}
cards={DUMMY_DATA}
stackSize={5}
cardIndex={0}
verticalSwipe={false}
animateCardOpacity
onSwipedLeft={() => {
console.log("Nay");
}}
onSwipedRight={() => {
console.log("Yay");
}}
overlayLabels={{
left: {
title: "NAY",
style: {
label: {
textAlign: "right",
color: "red", //make it purple
},
},
},
right: {
title: "YAY",
style: {
label: {
color: "purple",
},
},
},
}}
renderCard={(card) =>
card ? (
<View
key={card.id}
style={tailwind("relative bg-white-500 h-3/4 rounded-2xl")}
>
<Image
style={tailwind("absolute top-0 h-full w-full rounded-xl")}
source={{ uri: card.photoURL }}
/>
<View
style={tailwind(
"absolute bottom-0 bg-white w-full flex-row justify-between items-between h-20 px-6 py-2 rounded-b-xl"
)}
>
<View>
<Text style={tailwind("text-xl font-bold")}>
{card.name}
</Text>
</View>
<Text style={tailwind("text-2xl font-bold")}>
{card.address}
</Text>
</View>
</View>
) : (
<Text>ENDDDDddd</Text>
)
}
/>
</View>
<Button title="Places" onPress={() => this.handleRestaurantSearch()} />
</SafeAreaView>
);
};
export default HomeScreen;