I was successfully able to send data from Java to React-Native using a callback that invokes an array. I can display said data in the console, however I want to be able to display it inside the react-native component itself.
This is the Java method that is supposed to get all the IP addresses connected to my wifi (Thanks to JavaPF from javaprogrammingforums.com for the code)
#ReactMethod
public void displayConnectedDevices(Callback errorCallback, Callback successCallback) throws IOException {
WritableArray array = new WritableNativeArray();
try{
InetAddress localhost= InetAddress.getLocalHost();
byte[] ip = localhost.getAddress();
for(int i = 1; i <= 254; i++)
{
ip[3] = (byte)i;
InetAddress address = InetAddress.getByAddress(ip);
if (address.isReachable(1000))
{
System.out.println(address + "can be pinged");
array.pushString(address.toString());
}
else if(!address.getHostAddress().equals(address.getHostName()))
{
System.out.println(address + "this machine is known in a DNS lookup");
}
else
{
System.out.println(address + "host name could not be resolved");
}
}//end of for loop
successCallback.invoke(array);
} catch (IllegalViewOperationException e) {
errorCallback.invoke((e.getMessage()));
}
}
This is the React-Native method where I want to display the array:
//Importing native java module
import {NativeModules} from 'react-native';
var ConnectedDevicesList = NativeModules.ConnectedDevices;
let LanScanner = () => {
const [arr, setArray] = useState([])
displayActiveConnections = async () => {
ConnectedDevicesList.displayConnectedDevices( (array) => { setArray(array)}, (msg) => {console.log(msg)} );
}
return (
<ScrollView>
<View>
<TouchableOpacity onPress={ this.displayActiveConnections }>
arr.map((item,index)=><Text key= {"conlist"}>{item}</Text>)
</TouchableOpacity>
</View>
</ScrollView>
);
};
export default LanScanner
All the guides I found point to rendering this data on the console, but not in the actual component. What should I do if I want to display this data?
Thank you in advance.
You can use useState. useState is a react hook and on getting the value from the api set the value there. Looks like the api response is an array so you can use map and display the Text
export const SomeFunction{
const [arr, setArray] = useState([])
displayActiveConnections = async () => {
ConnectedDevicesList.displayConnectedDevices( (array) => {
setArray(array)
}, (msg) => {console.log(msg)} );
}
render() {
return (
<ScrollView>
<View>
<TouchableOpacity onPress={ this.displayActiveConnections }>
arr.map((item,index)=><Text key= {someId}>{item}</Text>)
</TouchableOpacity>
</View>
</ScrollView>
);
}
}
}
So I finally got it to work with useState after some trial and error. This however, gives me an error that "each child should have a unique key". I have yet to fix that, but at least now I can see the data in the component (which in this case is a list of IP Addresses). Thanks to brk for his help!
import {NativeModules, View, TouchableOpacity, Text, ScrollView, SafeAreaView} from 'react-native';
import LanScannerStyle from '../Styles/LanScannerStyles';
//Declaring new instance of Java Module
var ConnectedDevices = NativeModules.ConnectedDevices;
export const LanScanner = () => {
const [arr, setArray] = useState([]);
displayConnectionListFromJava = () => {
//Getting the data from native module, and passing it to the arr variable with useState
try{
ConnectedDevices.displayConnectedDevices( (arrayResponse) => {setArray(arrayResponse), console.log(arrayResponse)} );
}
catch(error)
{
console.log(error);
}
//Map the arr variable and render it as text
return(
<View>
{
arr.map((items, index) =>
<View>
<Text>{items}</Text>
</View>)
}
</View>
);
};
return(
<ScrollView>
<View>
{this.displayConnectionListFromJava()}
</View>
</ScrollView>
);
}
Related
I am trying to create an image grid like the following on React Native.
I have managed to extract the data from https://pokeapi.co/ using Axios. My code is as the following so far but doesnt seem to work. The code below retrieves data from the API and I have set that data to setPokemon (How to I access this data) I have tried to assign that data to {data} below to be used inside the flatlist but its not working. It doesnt seem to assign the data at all.
export default function App() {
const [pokemons, setPokemon] = useState([])
//Fetching Pokemon from online database
async function fetchPokemon() {
try {
const { data } = await axios.get('https://pokeapi.co/api/v2/pokemon?limit=50')
setPokemon(data.results) // ASSIGN DATA TO setPokemon
}
}
//Hook to fetch Pokemon upon component mount
useEffect(() => {
fetchPokemon()
}, [])
const renderPokemon = (item, index) => {
return <Text>{item.name}</Text>
}
const {data} = setPokemon // ALL POKEMON SHOULD BE INSIDE THIS
return (
<SafeAreaView>
<FlatList
style={styles.container}
data={data} // ALL POKEMON SHOULD BE INSIDE THIS
renderItem={renderPokemon}
keyExtractor={pokemons => `key-${pokemons.name}`}
>
</FlatList>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
Any tips on this?
You are trying to access data from the state setter function. After calling setPokemon(5), pokemons will be 5.
const {data} = pokemons expects your data to be an object, but you've initialized it as a list, and it looks like you're trying to populate it as a list. Do you mean to write const data = pokemons to simply rename it rather than destructuring it?
Assuming that data.results is a list of stuff, here's what the working component will look like:
function App() {
const [pokemons, setPokemon] = useState([]);
async function fetchPokemon() {
try {
const { data } = await axios.get('https://pokeapi.co/api/v2/pokemon?limit=50')
setPokemon(data.results)
}
}
useEffect(fetchPokemon, [])
const renderPokemon = (item, index) => {
return <Text>{item.name}</Text>
};
return (
<SafeAreaView>
<FlatList
style={styles.container}
data={pokemons} // ALL POKEMON SHOULD BE INSIDE THIS
renderItem={renderPokemon}
keyExtractor={pokemons => `key-${pokemons.name}`}
>
</FlatList>
</SafeAreaView>
);
};
Edit: it seems like there is another error related to object destructuring. If you look at the FlatList docs the renderItem requires a function with this signature: (o: {item: T, index: number}) => Element. Simply update your renderPokemon function.
I'm trying to call external api during the first time when the component is rendered and get the list of all countries with async/await function. However I got empty state back after I called the setState function. I'm quite new to RN and been stuck in this problem for days now. Appreciated if anyone can help. Here's what I've been trying to do...
Call countryapi and get the country name and iso2 (only country name and iso2 are available in the response)
After that call I wanna get more details about that specific country, such as iso3 and country flag, so I passed the parameter iso2.
After I destructured for country, iso3, and country flag after the second api call, I called setState each time I destructured for that specific country. However state is not getting updated.
import React, { useState, useEffect } from "react";
import {
Platform,
Text,
View,
TextInput,
StyleSheet,
FlatList,
} from "react-native";
import { FontAwesome } from "react-native-vector-icons";
import countryapi from "../src/countryapi";
const CountryScreen = () => {
const [countryDetails, setCountryDetails] = useState([]);
// Fetch country details { country name, iso3, iso2, country flag }
const countrySearchAPI = async () => {
try {
const countriesData = await countryapi.get();
const countries = countriesData.data;
for (const country in countries) {
const { iso2 } = countries[country];
let countriesDetailsData = await countryapi.get(`/${iso2}`);
let { name, iso3, emoji } = countriesDetailsData.data;
setCountryDetails([
...countryDetails,
{
countryName: name,
iso3,
iso2,
countryFlag: emoji,
},
]);
console.log(countryDetails);
}
} catch (e) {
console.log(e);
}
console.log(countryDetails);
};
useEffect(() => {
countrySearchAPI();
}, []);
return (
<View>
<View style={styles.searchSection}>
<FontAwesome name="search" size={30} />
<TextInput placeholder="Search for Country" style={styles.inputBox} />
</View>
{/* <FlatList
data={countryDetails}
renderItem={({ item }) => {
return (
<Text>
{item.countryName}
{item.countryFlag}
</Text>
);
}}
keyExtractor={(item) => item.id.toString()}
/> */}
</View>
);
};
I am new to react native and I want on the welcome screen of my application to click on a request to an api using axios and the data is saved in a variable using useState and then use this data in another class (AllProductCategory .js) without having to make the request back to the api.
I am using React native 0.62 hooks react navigation 5 and axios.
I have the following in the Navigation.js file. A context that has a useMemo as its value, which contains a return so that it returns an array with information that it brings from an application using axios. the class looks like this:
In this class skip certain lines of code that have nothing to do with the problem I am currently having.
export default function Navigation() {
const [allproducts, setAllproducts] = useState([]);
useEffect(() => {
const _loadAllCategories = async () => {
await axiosClient
.get("/service/product_available")
.then(function (response) {
console.log("Data antes de pasarlo al useState ", response.data);
setAllproducts(response.data);
console.log("Los productos son: ", allproducts);
})
.catch(function (error) {
console.log("Error obteniendo el token", error);
});
};
_loadAllCategories();
}, []);
const authContext = useMemo(
() => ({
getAllProducts: () => {
return allproducts;
},
}),
[]
);
return (
<AuthContext.Provider value={authContext}>
{state.isLoading ? (
<SplashStackScreen />
) : state.userToken == null ? (
<PrincipalStackScreen />
) : (
<MyDrawer />
)}
</AuthContext.Provider>
);
}
With this file what I want is for the data that brings all the products to be loaded when the splash screen is loading and so when I want to use this data on another screen, just call the context variable and return the data without having to make another request to the api.
Then in the class I implement the call of this data using the context
const { getAllProducts } = React.useContext(AuthContext);
const allProducts = getAllProducts();
The complete class is like this:
import React, { useState, useEffect } from "react";
import { View, Text, FlatList, StyleSheet, TouchableOpacity, Dimensions, Image } from "react-native";
import { AuthContext } from "../../context";
var { height, width } = Dimensions.get("window");
export default function AllProductCategoryScreen() {
const { getAllProducts } = React.useContext(AuthContext);
const allProducts = getAllProducts();
function Product_Category({ name, image }) {
console.log("name e image", name);
return (
<View>
<TouchableOpacity>
<Image style={styles.imageCategory} source={{ uri: image }} />
<Text>{name}</Text>
</TouchableOpacity>
</View>
);
}
return (
<View>
<Text>Todas las categorias</Text>
<View style={{ alignItems: "center" }}>
<FlatList
scrollEnabled={true}
numColumns={2}
data={allProducts}
renderItem={({ item }) => (
<Product_Category name={item.name} image={item.imagePath} />
)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
</View>
);
}
My app.js is as follows:
import React from 'react';
import Navigation from "./src/components/Navigation/Navigation"
export default function App() {
return <Navigation />
}
The problem that I currently have is that when I start my app, I show that the request is made with axios and it brings the data correctly, however the useState is not filled with the data that the axios responds to me (it prints []). However if I save changes being in the navigation.js class in visual code the variable allproducts of the navigation class is filled with the data correctly and therefore in the other class where I want to display the data, it paints the data correctly.
I need that when my app loads, the data that the api brings is saved and that when using it in the other class, these data remain so that they can be used and illustrate this data on the screen.
When the function passed to useMemo is created, it captures the first value of allproducts and will always have that value. It will always return [].
In order for useMemo to run again and capture a new value, add that value to the second argument, the array. React will call the memo function whenever a value in that array changes, and then getAllProducts will be created again and capture a new value of allproducts.
const authContext = useMemo(
() => ({
getAllProducts: () => {
return allproducts;
},
}),
[allproducts]
);
Using react hooks with firebase real time database with a project Im working on for the first time, the data is being retrieved and logs in the console. BUT I am unable go move out side the useEffect function to display values on the screen! Would really appreciate some help!
videoArray needs to be put in a FlatList, as you can see in the code bellow videoArray logs values in the console in useEffect function. However, once I move out that function to add it into a FlatList it is null because the values are in a local function.
My question is, how am I able to pass value of videoArray (in useEffect) into the FlatList?
import React, { useState, useEffect } from 'react'
import { FlatList, View, TouchableOpacity, Text, StyleSheet, SafeAreaView } from 'react-native';
import { Center } from '../components/Center'
import { Video } from 'expo-av';
import firebase from '../firebase'
const videoRef = firebase.database().ref('videoCollaction');
export const FeedScreen = ({ }) => {
let [videoArray, setVideo] = useState([]);
useEffect(() => {
videoRef.on('value', (childSnapshot) => {
videoArray = [];
childSnapshot.forEach((doc) => {
videoArray.push({
key: doc.key,
video: doc.toJSON().video,
});
})
})
// able to log values only here
console.log('working:', videoArray);
});
// get video uri from firebase (testing)
// readVideo = () => {
// var collection = firebase.database().ref("videoCollactionvideo" + "/video").orderByValue();
// console.log('uri', collection);
// }
return (
<SafeAreaView>
<Text>Feed Screen</Text>
{/* null here: need values to show up here*/}
{console.log(" test",videoArray)}
<FlatList
data={videoArray}
renderItem={({ item, index }) => {
return (
<View>
<Text style={{ fontSize: 35, color: 'red' }}>Video:...</Text>
<TouchableOpacity onPress={() => console.log('pressed')}><Text style={{ color: 'blue' }}>Expand</Text></TouchableOpacity>
</View>
);
}} keyExtractor={({ item }, index) => index.toString()}>
</FlatList>
</SafeAreaView>
);
}
Try this:
useEffect(() => {
const temp = []; // temp array
videoRef.on("value", childSnapshot => {
childSnapshot.forEach(doc => {
temp.push({
key: doc.key,
video: doc.toJSON().video
});
});
setVideo(temp); // update state array
});
}, []);
It seems like you are trying to update the State Hook (videoArray), but you are doing it the wrong way (it shouldn't be modified directly). Instead, use the setVideo update method which you created with the Hook (let [videoArray, setVideo] = useState([]);):
useEffect(() => {
videoRef.on('value', (childSnapshot) => {
newVideoArray = [];
childSnapshot.forEach((doc) => {
newVideoArray.push({
key: doc.key,
video: doc.toJSON().video,
});
})
})
// able to log values only here
console.log('working:', newVideoArray);
setVideo(newVideoArray);
});
Check out Using the Effect Hook for more information on how to use this specific hook (the Optimizing Performance by Skipping Effects section might be especially of interest).
In essence, this functionality is similar to your Functional Component's stateful counterparts (React.Component or React.PureComponent), where:
Constructor is the only place where you should assign this.state directly. In all other methods, you need to use this.setState() instead.
Try this:
import React, { useState, useEffect } from 'react'
import { FlatList, View, TouchableOpacity, Text, StyleSheet, SafeAreaView } from 'react-native';
import { Center } from '../components/Center'
import { Video } from 'expo-av';
import firebase from '../firebase'
const videoRef = firebase.database().ref('videoCollaction');
export const FeedScreen = ({ }) => {
let [videoArray, setVideoArray] = useState([]);
useEffect(() => {
videoRef.on('value', (childSnapshot) => {
const newVideoArray = [];
childSnapshot.forEach((doc) => {
newVideoArray.push({
key: doc.key,
video: doc.toJSON().video,
});
})
setVideoArray(newVideoArray);
})
// able to log values only here
console.log('working:', videoArray);
}, []);
console.log('State also working :) >> ', videoArray);
return (
<SafeAreaView>
<Text>Feed Screen</Text>
{/* null here: need values to show up here*/}
{console.log(" test",videoArray)}
<FlatList
data={videoArray}
renderItem={({ item, index }) => {
return (
<View>
<Text style={{ fontSize: 35, color: 'red' }}>Video:...</Text>
<TouchableOpacity onPress={() => console.log('pressed')}><Text style={{ color: 'blue' }}>Expand</Text></TouchableOpacity>
</View>
);
}} keyExtractor={({ item }, index) => index.toString()}>
</FlatList>
</SafeAreaView>
);
}
I am trying to call my fetchPlants function, but I cannot see to figure out why it is NOT being called.
/screens/RecipeScreen.js
import usePlants from '../hooks/usePlants';
// Call our custom hook
const [fetchPlants, plantResults] = usePlants();
// ...other code...
<RecipeSearch
recipeSearch={recipeSearch}
onRecipeSearchChange={setRecipeSearch}
onRecipeSearchSubmit={() => fetchPlants(recipeSearch)}
/>
/components/RecipeSearch.js
const RecipeSearch = ({
onRecipeSearchChange,
onRecipeSearchSubmit,
recipeSearch,
}) => {
return (
console.log(recipeSearch); // This prints out nicely...
<View>
<View>
<TextInput
placeholder='Find a plant...'
value={recipeSearch}
onChangeText={onRecipeSearchChange}
onEndEditing={onRecipeSearchSubmit}
/>
</View>
</View>
);
};
/hooks/usePlants.js
import { useState, useEffect } from 'react';
import plantsApi from '../api/plants';
export default () => {
const [plantResults, setPlantResults] = useState([]);
const fetchPlants = async searchTerm => {
console.log('searchTerm... HERE IS THE QUERY', searchTerm); // this never gets hit
try {
const response = await plantsApi.get('').then(response => {
console.log(response);
setPlantResults(response);
});
} catch (err) {
console.log(err);
}
};
return [fetchPlants, plantResults];
};
I initially thought that maybe I was calling fetchPlants() too early (before recipeSearch had any state), but I don't think so, because it is still able to console.log(searchRecipe) properly.
Update it was working ALL along. When I was testing it with the iOS simulator I needed to hit the "ENTER" key on my computer because I am using the React Native onEndEditing prop.