I'm new to React Native and I'm having some difficulties with react navigation.
I'm building a weather app that fetches data from an API. There are 6 cities that I need to render to the main screen and when you press on a city it should take you to a different screen with the weather for the day for that particular city.
I can't seem to wrap my head around the last part - how to navigate to a different screen with the data when pressing on that particular city
I will appreciate any help. Thanks!
App:
const App = () => {
const [weatherData, setWeatherData] = useState([]);
const netInfo = useNetInfo();
const dataFetchAndProcess = async url => {
let data = await fetchDataFunc(url);
storeASData(data);
let takeData = await getASData();
let processData = await processDataFunc(takeData);
return setWeatherData(processData);
};
useEffect(() => {
dataFetchAndProcess(fetchUrl);
}, []);
return (
<View style={styles.container}>
<Header />
{weatherData && (
<CitiesContainer weatherData={weatherData}></CitiesContainer>
)}
</View>
);
};
CitiesContainer - generates cities from flatlist:
const CitiesContainer = ({weatherData}) => {
return (
<FlatList
style={{flex: 1, width: '100%'}}
data={weatherData}
renderItem={({item}) => (
<TouchableOpacity>
<CityContainer
date={item.date}
forecast={item.forecast}
cityName={item.cityName}
cityImg={item.cityImg}
/>
</TouchableOpacity>
)}
keyExtractor={(item, index) => index.toString()}
/>
);
};
CityContainer:
const CityContainer = ({cityName, cityImg, date, forecast}) => {
return (
<NavigationContainer>
<CityContext.Provider value={cityName}>
<Stack.Navigator initialRouteName="City">
<Stack.Screen name={cityName}>
{props => <City {...props} cityImg={cityImg} cityName={cityName} />}
</Stack.Screen>
<Stack.Screen name={`${cityName} forecast`}>
{props => (
<ForecastContainer
{...props}
date={date}
cityName={cityName}
forecast={forecast}
/>
)}
</Stack.Screen>
</Stack.Navigator>
</CityContext.Provider>
</NavigationContainer>
);
};
City:
const City = ({cityName, cityImg, date, forecast}) => {
// const cityName = useContext(CityContext);
return (
<TouchableOpacity>
<Text>{cityName}</Text>
<CityImage cityImg={cityImg} />
</TouchableOpacity>
);
};
ForecastContainer:
const ForecastContainer = ({cityName, date, forecast}) => {
return (
<View>
<Text>{cityName}</Text>
<Text>{date}</Text>
<FlatList
style={{flex: 1, width: '100%'}}
data={forecast}
renderItem={({item}) => (
<Forecast temp={item.temp} time={item.time}></Forecast>
)}
keyExtractor={(item, index) => index.toString()}
/>
{/* <Forecast forecast={forecast}></Forecast> */}
</View>
);
};
Forecast:
const Forecast = ({temp, time}) => {
return (
<View>
<Text>{temp}</Text>
<Text>{time}</Text>
</View>
);
};
CodePudding user response:
First thing that you have to pay some attention to, is that you are creating multiple navigation containers. So, you'll not be able to navigate between screens like that. Try to move your navigation container for a high-level component, some component that wraps all your screens.
Besides, you'll need to specify the action of moving between the screens to your component on the press event.
Some documentation that can help you with more details:
Setup React Navigation: https://reactnavigation.org/docs/hello-react-navigation
Moving between screens: https://reactnavigation.org/docs/navigating/
CodePudding user response:
After hours of research and head-banging I figured it out. I needed to pass params to the route to make it show data specific to each city:
App
<NavigationContainer>
<View style={styles.container}>
<Header />
<Stack.Navigator>
<Stack.Screen name="Home">
{props => <CitiesContainer {...props} weatherData={weatherData} />}
</Stack.Screen>
<Stack.Screen name="Forecast">
{props => (
<ForecastContainer {...props} weatherData={weatherData} />
)}
</Stack.Screen>
</Stack.Navigator>
</View>
</NavigationContainer>
CityContainer - onPress calls the Forecast page with an optional second param. The second param is crucial in this case as it's used to determine what data to render on the Forecast page
const CityContainer = ({cityName, cityImg, navigation}) => {
return (
<TouchableOpacity
onPress={() => navigation.navigate('Forecast', {cityName})}>
<City
navigation={navigation}
cityName={cityName}
cityImg={cityImg}></City>
</TouchableOpacity>
);
};
ForecastContainer - takes in the weatherData array, as well as the route arguments. The route argument is necessary to obtain the cityName param
const ForecastContainer = ({weatherData, route}) => {
const {cityName} = route.params;
const cityFinder = data => {
return data.filter(obj => obj.cityName === cityName);
};
return (
<FlatList
ListHeaderComponent={
<>
<Text>{cityName}</Text>
<Text>{cityFinder(weatherData)[0].date}</Text>
</>
}
data={cityFinder(weatherData)[0].forecast}
renderItem={({item}) => <Forecast temp={item.temp} time={item.time} />}
keyExtractor={(item, index) => index.toString()}
/>
);
};