I'm trying to run a search with user input, but my searchInput variable is showing up as undefined? - react-native

I'm trying to run a search with user input, but my searchInput variable is showing up as undefined when I run the code. I can't figure out what I'm doing wrong. Thanks for your help!
Here's my code:
import React, { useState } from "react";
import {
TouchableOpacity,
StyleSheet,
View,
Modal,
TextInput
} from "react-native";
import Icon from "react-native-vector-icons/Feather";
import API_KEYS from "../utils/APIKeys";
const SearchScreen = ({ modalVisible, setModalVisible }) => {
const [searchInput, setSearchInput] = useState("");
const [searchResults, setSearchResults] = useState([]);
const searchPlaces = ({ searchInput }) => {
console.log(searchInput);
fetch(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${searchInput}&types=cities&key=${API_KEYS.GOOGLE_MAPS_API_KEY}`
)
.then(res => res.json())
.then(json => {
console.log(json);
});
};
return (
<Modal animationType="fade" transparent={false} visible={modalVisible}>
<TouchableOpacity
style={styles.iconContainer}
onPress={() => setModalVisible(false)}
>
<Icon name="x" size={30} />
</TouchableOpacity>
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Search places…"
placeholderTextColor="#666"
value={searchInput}
onChangeText={newValue => setSearchInput(newValue)}
onEndEditing={searchInput => searchPlaces(searchInput)}
/>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
height: "100%",
justifyContent: "center",
marginHorizontal: 20
},
input: {
fontFamily: "hermitRegular",
fontSize: 24
},
iconContainer: {
zIndex: 10,
position: "absolute",
top: 60,
right: 25
}
});
export default SearchScreen;
Here's the response. The first line is the content of searchInput and the second is the response from the Google API.
undefined
Object {
"predictions": Array [],
"status": "INVALID_REQUEST",
}

I figured out what was wrong.
This needed updating:
const searchPlaces = ({ searchInput }) => {
console.log(searchInput);
fetch(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${searchInput}&types=cities&key=${API_KEYS.GOOGLE_MAPS_API_KEY}`
)
.then(res => res.json())
.then(json => {
console.log(json);
});
};
To this:
const searchPlaces = () => {
console.log(searchInput);
fetch(
`https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${searchInput}&types=(cities)&key=${API_KEYS.GOOGLE_MAPS_API_KEY}`
)
.then(res => res.json())
.then(json => {
console.log(json);
});
};

Related

how to use react native redux in react navigation

I am using redux persist with asyncstorage save items in a bookmarks list. The items are in a flatlist and when I click on one item, it navigates me to another screen. I would like to implement the functional bookmark in the header of that screen.
When I tried doing this, and clicked the bookmark in the header, and go back to the bookmarks, it just shows a blank card. It looks like it is not updating the state properly. How can I fix this?
StackNavigator.tsx
const MainStackNavigator = () => {
const { books, bookmarks } = useAppSelector((state) => state.booksReducer);
const dispatch = useDispatch();
const fetchBooks = () => dispatch(getBooks());
const addToBookmarkList = (book) => dispatch(addBookmark(book));
const removeFromBookmarkList = (book) => dispatch(removeBookmark(book));
useEffect(() => {
fetchBooks();
}, []);
const handleAddBookmark = (book) => {
addToBookmarkList(book);
};
const handleRemoveBookmark = (book) => {
removeFromBookmarkList(book);
};
const handleSwapBookmark = (book) => {
removeFromBookmarkList(book);
};
const RenderItem = () => {
const ifExists = (book) => {
if (bookmarks.filter((item) => item.id === book.id).length > 0) {
return true;
}
return false;
};
return (
<TouchableOpacity
onPress={() =>
ifExists(i) ? handleRemoveBookmark(i) : handleAddBookmark(i)
}
activeOpacity={0.7}
style={{
flexDirection: "row",
padding: 2,
backgroundColor: ifExists(i) ? "#F96D41" : "#2D3038",
borderRadius: 20,
alignItems: "center",
justifyContent: "center",
height: 40,
width: 40,
}}
>
<MaterialCommunityIcons
color={ifExists(i) ? "white" : "#64676D"}
size={24}
name={ifExists(i) ? "bookmark-outline" : "bookmark"}
/>
</TouchableOpacity>
);
};
return (
<AppStack.Navigator>
<AppStack.Screen
name="BookmarksScreen"
component={BookmarksScreen}
options={{
title: "Search",
statusBarColor: isDarkMode ? "white" : "black",
headerLargeTitle: true,
headerTranslucent: true,
headerLargeTitleHideShadow: true,
}}
/>
<AppStack.Screen
name="Screen2"
component={Screen2}
options={({ route }) => ({
headerLargeTitle: false,
title: route.params.name,
headerTranslucent: true,
headerRight: () => <RenderItem item={route.params.name} />,
})}
/>
</AppStack.Navigator>
);
};
actions.js
import axios from "axios";
import { BASE_URL } from "../config";
// Define action types
export const GET_BOOKS = "GET_BOOKS";
export const ADD_TO_BOOKMARK_LIST = "ADD_TO_BOOKMARK_LIST";
export const REMOVE_FROM_BOOKMARK_LIST = "REMOVE_FROM_BOOKMARK_LIST";
export const SWAP_IN_BOOKMARK_LIST = "SWAP_IN_BOOKMARK_LIST";
export const getBooks = () => {
try {
return async (dispatch) => {
const response = await axios.get(`${BASE_URL}`);
if (response.data) {
dispatch({
type: GET_BOOKS,
payload: response.data,
});
} else {
console.log("Unable to fetch data from the API BASE URL!");
}
};
} catch (error) {
console.log(error);
}
};
export const addBookmark = (book) => (dispatch) => {
dispatch({
type: ADD_TO_BOOKMARK_LIST,
payload: book,
});
};
export const removeBookmark = (book) => (dispatch) => {
dispatch({
type: REMOVE_FROM_BOOKMARK_LIST,
payload: book,
});
};
hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
reducers.ts
import {
GET_BOOKS,
ADD_TO_BOOKMARK_LIST,
REMOVE_FROM_BOOKMARK_LIST,
} from "./actions";
const initialState = {
books: [],
bookmarks: [],
};
function booksReducer(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
return { ...state, books: action.payload };
case ADD_TO_BOOKMARK_LIST:
return { ...state, bookmarks: [...state.bookmarks, action.payload] };
case REMOVE_FROM_BOOKMARK_LIST:
return {
...state,
bookmarks: state.bookmarks.filter(
(book) => book.id !== action.payload.id
),
};
default:
return state;
}
}
export default booksReducer;
store.ts
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-async-storage/async-storage";
import { persistStore, persistReducer } from "redux-persist";
import booksReducer from "./reducers";
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["bookmarks"],
};
const rootReducer = combineReducers({
booksReducer: persistReducer(persistConfig, booksReducer),
});
export const store = createStore(rootReducer, applyMiddleware(thunk));
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
BookmarksScreen.tsx
const BookmarksScreen = () => {
return (
<View>
<FlatList
data={bookmarks}
keyExtractor={(item) => item.id}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
/>
</View>
);
}
renderItem
const renderItem = ({ item }) => {
return (
<TouchableOpacity
onPress={() =>
navigation.navigate("Screen2", {name: item.name})}
>
<View style={{ flexDirection: "row", flex: 1 }}>
<View>
<Text
style={{
fontSize: 22,
paddingRight: 16,
color: "black",
fontFamily: "Medium",
left: 45,
top: 6,
}}
>
{item.country}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
};
I think you forgot to access props that's the reason booked-marked not working i change some code please check it's working or not.
StackNavigator.tsx
const RenderItem = (item) => {
const ifExists = (book) => {
if (bookmarks.filter((item) => item.id === book.id).length > 0) {
return true;
}
return false;
};
return (
<TouchableOpacity
onPress={() =>
ifExists(item) ? handleRemoveBookmark(item) : handleAddBookmark(item)
}
activeOpacity={0.7}
style={{
flexDirection: "row",
padding: 2,
backgroundColor: ifExists(i) ? "#F96D41" : "#2D3038",
borderRadius: 20,
alignItems: "center",
justifyContent: "center",
height: 40,
width: 40,
}}
>
<MaterialCommunityIcons
color={ifExists(i) ? "white" : "#64676D"}
size={24}
name={ifExists(i) ? "bookmark-outline" : "bookmark"}
/>
</TouchableOpacity>
);
};

How to display array elements in react native using FlatList

I'm trying to fetch data from an API and display that in a FlatList component in react. But when displaying data in the array, it displays an empty screen. Below is my code
useEffect(() => {
axios.get(`https://api.github.com/users/${user}/repos`)
.then(response => {
console.log(response.data)
response.data.map(repo => {
let rep = {
id:repo.id,
name:repo.name,
created:repo.created_at,
url:repo.html_url
}
repos.push(rep)
})
console.log(repos)
})
.catch(err => {
setError(true)
console.log(err)
})
},[])
return (
<View>
<Header navigate={navigation}/>
<FlatList
contentContainerStyle={{backgroundColor:'grey',marginTop:25}}
data={repos}
renderItem={({repo}) => (
<Text>
{repo.name}
</Text>
)}
/>
</View>
)
Here is the full working example: Expo Snack
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import axios from 'axios';
export default function App() {
const [repos, setRepos] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
axios
.get(`https://api.github.com/users/theketan2/repos`)
.then((response) => {
let data = [];
//console.log(response.data);
response.data.map((repo) => {
let rep = {
id: repo?.id,
name: repo?.name,
created: repo?.created_at,
url: repo?.html_url,
};
data.push(rep);
});
// console.log(data);
setRepos(data);
})
.catch((err) => {
setError(true);
console.log(err);
});
}, []);
useEffect(() => {
console.log(repos);
}, [repos]);
return (
<View style={styles.container}>
<FlatList
data={repos}
renderItem={({item}) => (
<View style={styles.list}>
<Text>{item?.name}</Text>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(21,21,21,0.1)',
},
list: {
padding: 10,
marginTop: 5,
borderRadius: 5,
backgroundColor: 'white',
marginHorizontal: 5,
},
});
Try this way
const [data, setData] = useState([]);
useEffect(() => {
axios.get(`https://api.github.com/users/${user}/repos`)
.then(response => {
setData(response.data);
})
.catch(err => {
setError(true)
console.log(err)
})
},[])
return (
<View>
<Header navigate={navigation}/>
<FlatList
data={data}
renderItem={(item) => (
<Text>
{item.name}
</Text>
)}
/>
</View>
)

Searching in Expo's FlatList

I used this tutorial but it didn't work (if you are interested, check out my last post). Any suggestions to make a working search for a flatlist?
I have a list of 100 things and just by inserting the name in a search bar, the flatlist should update with the results.
Try using react-native-searchable-flatlist
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;

React-Native Searchable FlatList with Local Json File

I am trying to create a searchable flatlist on this new app I was working on over quarantine. I followed an article on the internet on how to create it and my code so far looks like this:
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;
This works and all, but how could I alter the makeRemoteRequest function so that I got data from a local json file instead of a url? For example:
makeRemoteRequest = () => {
const url = `../data/userData.json`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
I tried this with no success as none of the json data rendered and appeared in the flatlist
The Fetch API is a promise-based JavaScript API for making asynchronous HTTP requests in the browser similar to XMLHttpRequest. If you want to load data from a local JSON file.
First import that file
import Data from '../data/userData.json'
Then assign imported value to your state inside your makeRemoteRequest function
makeRemoteRequest = () => {
this.setState({
data: Data,
});
}
Hope this helps you. Feel free for doubts.

How to update state react native hooks from other screen using react navigation hook param?

How to update state react native hooks from other screen using react navigation hook param?
I am trying to update state selectedHotel in screen 1 from screen 2 that provide the data, so i save data from screen 2 in parameter using react navigation hooks params, the data is update but i can't update state selectHotel if data is exist in useEffect screen 1, here the code:
screen 1:
import {useNavigation, useNavigationParam} from 'react-navigation-hooks';
const TransportScreen = () => {
const hotelParam = useNavigationParam('hotel');
const [baseLoading, setBaseLoading] = useState(true);
const [selectedHotel, setSelectedHotel] = useState(
hotelParam ? hotelParam.id : '',
);
const {navigate} = useNavigation();
useEffect(() => {
setTimeout(() => {
setBaseLoading(false);
}, 1000);
if (hotelParam) {
setSelectedHotel(hotelParam.id);
console.log('update selected hotel', selectedHotel);
}
}, []);
const redirectTransportSelectHotel = () => {
console.log('select hotel');
navigate('TransportSelectHotel');
};
const submitTransport = () => {
console.log('getHotelId ', selectedHotel);
};
const renderContent = () => {
console.log('hotelId: ', selectedHotel);
if (!baseLoading) {
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
}}>
<MenuCard
expandRight
onPress={() => redirectTransportSelectHotel()}>
{hotelParam ? hotelParam.name : 'Select Hotel'}
</MenuCard>
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<View
style={{
flexDirection: 'row',
marginHorizontal: 40,
marginVertical: 20,
}}>
<Button onPress={() => submitTransport()}>Submit</Button>
</View>
</View>
</View>
);
}
return <LoaderScreen visible={baseLoading} />;
};
};
screen 2:
import {useNavigation, useNavigationParam} from 'react-navigation-hooks';
import {useSelector, useDispatch} from 'react-redux';
import {getHotels} from './actions';
import _ from 'lodash';
const TransportSelectHotelScreen = () => {
const {navigate} = useNavigation();
const dispatch = useDispatch();
const [baseLoading, setBaseLoading] = useState(true);
const {hotel} = useSelector(state => ({
hotel: state.transportHotelReducer,
}));
useEffect(() => {
setTimeout(() => {
setBaseLoading(false);
}, 1000);
loadHotels();
}, [loadHotels]);
const handleRefresh = () => {
console.log('refresh');
loadHotels();
};
const loadHotels = async () => {
dispatch(getHotels());
};
const redirectTransportCallback = hotel => {
console.log('hotel detail', hotel);
navigate('Transport', {hotel: hotel});
};
const renderItem = item => {
return (
<MenuCard
expandRight
onPress={() => redirectTransportCallback(item.item)}>
{item.item.name}
</MenuCard>
);
};
const renderContent = () => {
if (!baseLoading) {
if (!hotel.hotels.baseLoading) {
if (!_.isEmpty(hotel.hotels)) {
return (
<View style={globalStyles.menuContainer}>
<FlatList
data={hotel.hotels}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
refreshing={hotel.isRefreshing}
onRefresh={handleRefresh}
// onEndReached={handleLoadMore}
// onEndReachedThreshold={0.1}
/>
</View>
);
} else {
return (
<View style={globalStyles.wrapperContent}>
<Text>{Lang.no_data}</Text>
</View>
);
}
}
return <LoaderScreen visible={hotel.baseLoading} />;
}
return <LoaderScreen visible={baseLoading} />;
};
};
You could try
useEffect(() => {
setSelectedHotel(hotelParam ? hotelParam.id : '')
}, [hotelParam])