ref undefined in React Native - react-native

Can someone help me in figuring out wha is the best way to use setNativeProps in RN?.
In my react function component I did something like this
const AutoComplete = (props) => {
let parentViewRef = null
useEffect(() => {
setTimeout(function(){ console.log(parentViewRef) }, 3000)
},[])
return(
<View
ref={(viewRef) => {
parentViewRef = viewRef
}}
style={styles.modelOpenViewMain}> <View style={styles.modelOpenInputView}>
</View>
</View>
)
}
}
But unfortunately my parentViewRef is coming out to be undefined. Any idea what I could be doing wrong?

If I'm understanding correctly what you're trying to achieve, then you should be using useRef hook.
So your code would look like something like this:
import React, { useRef } from 'react';
const AutoComplete = (props) => {
let parentViewRef = useRef(null);
return(
<View
ref={parentViewRef}
style={styles.modelOpenViewMain}> <View style={styles.modelOpenInputView}>
</View>
</View>
)
}
}
You will then be able to do whatever you want with your ref via its current property, e.g.
parentViewRef.current. ...
Hope this helps!

Try Passing the parentViewRef as an argument to the useEffect method.
const AutoComplete = (props) => {
let parentViewRef = null
useEffect(() => {
console.log(parentViewRef);
}, [parentViewRef]);
return (
<View
ref={(viewRef) => parentViewRef = viewRef}>
<View >
<Text>CONTENT TEXT</Text>
</View>
</View>
)
}

Related

Use .map not flatlist with activityIndicator in react native

I am getting a list of data using map not flatlist, I want to use .map not flatlist, when I apply ActivityIndicator to it while fetching the data, it did not work see below
Below is my code
<View>
{dataList.map((dataList, index) =>
<Text key={index} >{dataList.category_name}</Text>
<Text key={index} >{dataList.category_number}</Text>
)}
</View>
When I tried it with ActivityIndicator see below
{ dataList ?
<View>
{dataList.map((dataList, index) =>
<Text key={index} >{dataList.category_name}</Text>
<Text key={index} >{dataList.category_number}</Text>
)}
</View>
:
<ActivityIndicator />
}
It the not work, I will need your help with this.
Thanks in advance
Try using a boolean for your conditional render such as a loading state which you can easily toggle on and off at the beginning of the fetch and at the end respectively. You can also state the logic of your map outside of your component so it looks way cleaner and easy to read like this example:
import React from 'react';
import { Text, View, ActivityIndicator } from 'react-native';
const dataList = [
{ category_name: 'pop', category_number: 1 },
{ category_name: 'dance', category_number: 2 },
{ category_name: 'hiphop', category_number: 3 },
];
const renderDataList = () => {
return dataList.map((dataList, index) => (
<View>
<Text>{dataList.category_name}</Text>
<Text>{dataList.category_number}</Text>
</View>
));
};
const App = () => {
const [isLoading, setIsloading] = React.useState(false);
return <View>{isLoading ? <ActivityIndicator /> : renderDataList()}</View>;
};
export default App;
Your output would be for false:
Your output for true would be:

How to pass selected data to another screen from Flatlist

I am still new in using React Native and Mobile Apps Development. I tried to copy the code from another tutorial and have little bit of understanding it.
I have Save.js, Feed.js and Details.js. I have successfully retrieved the data from Save.js to Feed.js using FlatList and RenderItem. Now, I want to pass only selected data from Feed.js to Details.js. But I am confused which way to use, whether useNavigation, getParam, withNavigation or anything else? And is there any difference between using Hooks and Class? Btw I'm using Hooks.
Save.js
import { View, TextInput, Image, Button, StyleSheet, TouchableOpacity, Text} from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
export default function Save(props, navigation) {
const [productName, setProductName] = useState("")
const [category, setCategory] = useState("")
return (
<View style={styles.inputView}>
<TextInput
placeholder="Product name..."
onChangeText={(productName) => setProductName(productName)}
/>
</View>
<View style={styles.inputView}>
<TextInput
placeholder="Category..."
onChangeText={(category) => setCategory(category)}
/>
</View>
Feed.js
function Feed(props, navigation) {
const { currentUser, posts } = props;
const { navigate } = useNavigation();
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
Details.js
export default function Details({ props, navigate, route }) {
const productName = props.navigation.route.params.productName;
const { navigate } = useNavigation();
const productName = useNavigationParam('productName');
return (
<View>
<Text>{productName}</Text>
<Text>{Category}</Text>
</View>
)
}
I am not sure which way to use in Details.js, so I just put all code I have used and tested.
the code bellow will help you and I think you have problem in destructing context this will help you. and remember navigation is an object inside props
Feed.js
function Feed(props) {
const { currentUser, posts, navigation } = props;
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
in Feed you dont need to use useNavigation() because props argument contain navigation.
Details.js
export default function Details(props) {
const {productName, category} = props.navigation.route.params;
return (
<TouchableOpacity onPress={()=>props.navigation.navigate("Save",{productName, category})}>
<Text>{productName}</Text>
<Text>{Category}</Text>
</TouchableOpacity>
)
}

Using memoizing selectors with Hooks in React Native

One of the components in my react native app is re-rendering several times causing problems with my derived data.
I'm using Redux to store my state and useSelector hook to retrieve the state and use it during rendering. I've read quite a bit about the use of Reselect library to avoid unnecessary rendering and optimise performance but I'm struggling to apply to my ES6 code with hooks.
This is my current code
import { useSelector, useDispatch } from "react-redux";
import...
const MovieDetailScreen = (props) => {
const selectedMovie = useSelector((state) => state.moviemain.moviemain);
const selectedMovieCast = useSelector((state) => state.moviecast.moviecast);
const selectedMovieCrew = useSelector((state) => state.moviecast.moviecrew);
return (
<View style={styles.container}>
<View>
<Text style={styles.description} numberOfLines={3}>
{selectedMovie.name}
</Text>
</View>
<View>
<Text style={styles.description} numberOfLines={3}>
{selectedMovie.overview}
</Text>
</View>
<View>
<Text style={styles.description} numberOfLines={3}>
{selectedMovie.released_date}
</Text>
</View>
<View style={styles.textLabelRow}>
{selectedMovie.genres.map((item, id) => {
return (
<Text
style={[styles.txtLabel, { backgroundColor: "#404040" }]}
key={id}
numberOfLines={1}
>
{item.name}
</Text>
);
})}
</View>
</View>
...
...
);
};
I would like to apply the Reselect to any derived data, in the example code attached it would be the mapping processing of the genres parameter of the selectedMovie state
{selectedMovie.genres.map((item, id) => {
return (
<Text
style={[styles.txtLabel, { backgroundColor: "#404040" }]}
key={id}
numberOfLines={1}
>
{item.name}
</Text>
);
})}
I have another dozens of similar scenarios where I need to filter data or work out totals and due to re-rendering I often get errors.
I believe that using Reselect, the function would only be executed if the state changes.
I tried to follow the example in here by moving my state selection outside my component and restructure my code like this
import...
import { createSelector } from "reselect";
const getMovie = createSelector(
(state) => state.moviemain.moviemain,
(moviemain) => moviemain.moviemain.map((item) => item.genres)
);
export const GenresList = () => {
const genres = useSelector(getMovie);
return (
<Text
style={[styles.txtLabel, { backgroundColor: "#404040" }]}
numberOfLines={1}
>
{genres}
</Text>
);
};
const MovieDetailScreen = (props) => {
const selectedMovie = useSelector((state) => state.moviemain.moviemain);
const selectedMovieCast = useSelector((state) => state.moviecast.moviecast);
const selectedMovieCrew = useSelector((state) => state.moviecast.moviecrew);
return (
<View style={styles.container}>
....
....
<View>
<GenresList />
</View>
</View>
...
...
);
};
but I'm getting the following error in the createSelector function
undefined is not an object (evaluating 'moviemain.moviemain.map')
I've tried other suggested solutions having all code within the main components but I get other types of errors.
I'd appreciate some guidance.
TLDR;
In the following code the first argument returns the moviemian.moviemain property, and the next line you want to get the moviemain property of that - meaning: moviemian.moviemain.moviemain which is undefined so you cant map it.
const getMovie = createSelector(
(state) => state.moviemain.moviemain,
(moviemain) => moviemain.moviemain.map((item) => item.genres)
);
Remember: what you write in a selector you get the result in the second argument.
Solution: remove the extra moviemain:
(moviemain) => moviemain.map((item) => item.genres)
Redux selectors can be tricky, here's a refresher
// you can either use multiple selectors
// declare these outside of component
const getSelectedMovie = (state) => state.moviemain.moviemain;
const getSelectedMovieCast = (state) => state.moviecast.moviecast;
const getSelectedMovieCrew = (state) => state.moviecast.moviecrew;
// or use one since its the same object
const getSelectedMovieAll = (state) => state.moviemain;
// so that you can use them inside component like
const MovieDetailScreen = (props) => {
const selectedMovie = useSelector(getSelectedMovie);
const movies = useSelector(getSelectedMovieAll);
// pay attention to the keys, they remain the same
const { moviecast: selectedMovieCast, moviecrew: selectedMovieCrew } = movies;
}
Well, that's for the redux part, about reselect, you can use it like this:
const example = createSelector(
[selector1, selector2]
(resultOfSelector1, resultOfSelector2) => ({ 'return': 'something'})
);
// so in case moviemain.moviemain is an array you can do the following
// make sure you reuse the previous selectors
const getMovie = createSelector(
[getSelectedMovie]
(moviemain) => moviemain.map((item) => item.genres)
);
export const GenresList = () => {
const genres = useSelector(getMovie);
return (

Can't perform a React state update on an unmounted component. Cancel all tasks in a useEffect

I have seen is a common error and I tried different solutions with no result.
This is my code so far, rarely is working and the fetch is returning a proper movies array but most of the times is sending back an error:
import React, { useState, useEffect } from "react";
import { Text, View, Image, ScrollView, ActivityIndicator } from "react-native";
function Dashboard() {
const [loading, setLoading] = useState(true);
const [popularMovies, setPopularMovies] = useState([])
const popularMoviesUrl =
".....";
const fetchMovies = () => {
fetch(popularMoviesUrl)
.then(res => res.json())
.then(setPopularMovies)
.then(console.log(popularMovies));
};
useEffect(() => {
fetchMovies();
}, []);
const { results } = popularMovies;
return loading ? (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#dcae1d" animating />
</View>
) : (
<ScrollView horizontal style={styles.container}>
{results.map(movie => (
<View key={movie.id}>
<Text style={styles.container}>{movie.title}</Text>
<Image
style={styles.poster}
source={{
uri: `https://image.tmdb.org/t/p/w500${movie.poster_path}`
}}
/>
</View>
))}
<Text>thfh</Text>
</ScrollView>
);
}
export default Dashboard;
Seems to be an issue referencing the popular movies returned from your fetch. Set your popularMovies state as the results property from your fetch rather than the for JSON.
So change:
.then(setPopularMovies)
to...
.then(resJson => setPopularMovies(resJson.results))
Then remove the results variable and reference popularMovies directly.

Error when call a action come from a other file on react native

On index.js
...
import ButtonContent from './ButtonContent';
...
class App extends Component {
onAlert() {
alert("Test")
}
render() {
return (
<View>
<ButtonContent/>
</View>
)
}
}
file ButtonContent.js
...
import { withNavigation } from "react-navigation";
...
const ButtonContent = () => (
<TouchableOpacity onPress={() => {
this.onAlert();
}}>
<Text>Alert</Text>
</TouchableOpacity>
);
export default withNavigation(ButtonContent);
Error this.onAlert() is not function. How to fix it?
You need to pass onAlert function to ButtonContent component,
<ButtonContent onAlert={this.onAlert}/>
And then you can call this function using props,
const ButtonContent = (props) => (
<TouchableOpacity onPress={props.onAlert}>
<Text>Alert</Text>
</TouchableOpacity>
);
#ravibagul91 thanks your ideas, I has fixed ok. This is my edited code
file ButtonContent.js
type Props = {
onAlert: Function
};
const ButtonContent = ({ onAlert }: Props): Object => (
<TouchableOpacity onPress={props.onAlert}>
<Text>Alert</Text>
</TouchableOpacity>
);
And index.js
<ButtonContent onAlert={this.onAlert}/>