How do I pass a string to React Native function? - react-native

I have three buttons which should pass a string value to a function that sorts the movies I have in my app based on the word (it starts a query).
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
useEffect(() => {
getMovies();
},[])
useEffect(() => {
getMoviesFiltered();
},[])
const [filter, setFilter] = useState('');
const getMoviesFiltered = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
const getMovies = async () =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy('name')));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name')}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime')}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year')}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen
I can see that it updates the filter value when I press the button twice (I've read that setState is an asynchronous operation, so I get why that happens), but I'm not sure how to properly pass that string into the getMovies1 function.
Could someone please help me out?

It looks like you are trying to pass the filter state value to the getMoviesFiltered function when it is called. However, you are not passing any arguments to the getMoviesFiltered function when you call it in the TouchableOpacity onPress event handlers.
To fix this, you can pass the filter state value as an argument to the getMoviesFiltered function in the onPress event handlers like this:
<TouchableOpacity onPress = {() => {setFilter('name');
getMoviesFiltered(filter)}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime');
getMoviesFiltered(filter)}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year');
getMoviesFiltered(filter)}}>
<Text>Year</Text>
</TouchableOpacity>
You may also want to consider moving the useEffect hook that calls getMoviesFiltered to depend on the filter state value. This will ensure that getMoviesFiltered is only called when the filter state value changes. You can do this like this:
useEffect(() => {
getMoviesFiltered(filter);
},[filter])

Related

How do I get setState to update a value immediately?

I have to press the buttons twice to update the filter value for how I want to display the movies in my app. This is my code:
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
useEffect(() => {
getMovies();
},[])
useEffect(() => {
getMoviesFiltered(filter);
},[filter])
const [filter, setFilter] = useState('name');
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name'); getMovies(filter)}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime'); getMovies(filter)}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year'); getMovies(filter)}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen
I know that setState is asynchronous and that that is the reason it happens, but I'm kind of stuck on not knowing how to change it properly, so I'd appreaciate the help. Thank you.
useEffect will run when the values within dependency array change.
So, you don't need to add function getMovies after you changed your filter. Just simply move that to useEffect.
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
const [filter, setFilter] = useState('name');
useEffect(() => {
//when page is initialize, run this
getMovies(filter);
},[])
useEffect(() => {
//when filter is changed, use latest value to run this
getMovies(filter);
},[filter]);
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name');}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime');}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year');}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen

React Native Hooks - Trying to refactor from class Component to Function Component to use hooks

I'm new in react native and I'm tryng to refactor this code below, and I think I'm doing some thing wrong here "setSentences(item)", because it's not updating.
Do you know what i'm doing wrong here?
this.state = {
sentences: [],
};
{this.state.sentences.map((item) => {
return(
<TouchableOpacity
onPress={() => {
item.selected = false;
this.setState(item);
}}>
</TouchableOpacity>
)}}
Refactored:
const [sentences, setSentences] = useState([]);
{sentences.map((item) => {
return (
<TouchableOpacity
onPress={() => {
item.selected = false;
setSentences(item);
}}>
</TouchableOpacity>
sentences is an array but when you show list array setSentences update array sentences. Something wrong here
const [sentences, setSentences] = useState([]);
{sentences.map((item) => {
return (
<TouchableOpacity
onPress={() => {
item.selected = false;
setSentences(item);
}}>
</TouchableOpacity>
I think you can use useRef to store array sentences and another state to set item
const sentences = useRef([]);
const [itemSentence, setItemSentence] = useState('');
<Text>{itemSentence}</Text>
{sentences.current.map((item) => {
return (
<TouchableOpacity
onPress={() => {
item.selected = false;
setItemSentence(item);
}}>
</TouchableOpacity>
For clear solution you have to create one method like this :
const onItemPress = (itemIndex) => {
const temp = [].concat(sentences);
temp[itemIndex].selected = false;
setSentences(temp);
}
Now, call this method on TouchableOpacity onPress method and pass current index to this method to modify that perticular item state as below :
{sentences.map((item, itemIndex) => {
return (
<TouchableOpacity
onPress={() => onItemPress(itemIndex)}>
</TouchableOpacity>
)
}}
And note, your state should be :
const [sentences, setSentences] = useState([]);
i think you need clone array sentences and get index inside
const [sentences, setSentences] = useState([]);
sentences.map((item:any,index:number) => {
return (
<TouchableOpacity
onPress={() => {
const cloneSentences =clone(sentences);
cloneSentences[index].selected = false;
setSentences(cloneSentences);
}}>
</TouchableOpacity>)
})

How to access one functional component's state from another functional component in react-native?

I want to access ModalView's state from MarkerView component. Actually i want to see ModalView when i click Get info. button which is in MarkerView. I want to set { setVisiblity(true)} from MarkerView component. How can i do it?
ModalView.js
const ModalView = () => {
const [visiblity, setVisiblity] = useState(false);
return (
<Modal transparent={false} visible={visiblity} >
<TouchableOpacity onPress={() => { setVisiblity(false)}}>
<Text> Submit </Text>
</TouchableOpacity>
</Modal>
)
}
MarkerView.js
const MarkerView = () => {
return (
// i want to set visiblity true from here
<View>
<TouchableOpacity onPress={() => { setVisiblity(true) }}>
<Text>Get info.</Text>
</TouchableOpacity>
</View>
)
}
App.js
import ModalVIew from './components/ModalView';
import Marker from './components/MarkerView';
const App = () => {
return (
<View>
<Marker/>
<ModalVIew/>
</View>
)
}
export default App;
you can use state management like contextAPI or redux, or you can put your state on your higher order component but this will result in some prop drilling.
App.js
const App = () => {
const [visiblity, setVisiblity] = useState(false);
return (
<View>
<Marker visiblity={visiblity} onChangeVisiblity={(val) => setVisiblity(val)}/>
<ModalVIew visiblity={visiblity} onChangeVisiblity={(val) => setVisiblity(val)}/>
</View>
)
}
MarkerView.js
const MarkerView = ({visiblity, onChangeVisiblity: changeVisiblity}) => {
return (
<View>
<TouchableOpacity onPress={() => changeVisiblity(true)}>
<Text>Get info.</Text>
</TouchableOpacity>
</View>
)
}
ModalView.js
const ModalView = ({visiblity, onChangeVisiblity:changeVisiblity}) => {
const [visiblity, setVisiblity] = useState(false);
return (
<Modal transparent={false} visible={visiblity} >
<TouchableOpacity onPress={() => changeVisiblity(false)}>
<Text> Submit </Text>
</TouchableOpacity>
</Modal>
)
}

React Native Function Component ASYNC / AWAIT Problem

sorry for bad English.
My function component not waiting API Function, I'm write async and await and not working again..
"Object are not valid as a React child" error screen...
Please help me :'/
const NormalCarousel = async (props) => {
const navigation = useNavigation();
const [ResponseData, setResponseData] = useState('');
const ComponentAPI = props.api;
const API = await axios.get(ComponentAPI).catch((error) => {alert(error)});
await setResponseData(API);
return(
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
{
ResponseData.map(item => (
<TouchableOpacity style={styles.CarouselTouchable} onPress={() => navigation.navigate("Ürün", {id: item.item_id})}>
<Image
style={styles.CarouselImage}
source={{uri: item?.item_avatar}}
/>
<View style={styles.CarouselView}>
<Text style={styles.CarouselTitle}>{item?.item_name}</Text>
<Text style={styles.CarouselSubtitle}>{item?.item_stock_code}</Text>
</View>
</TouchableOpacity>
))
}
</ScrollView>
)
}
The error is happening because the parent component trying to render this component is actually rendering a Promise which resolves into a component. That's not possible.
You should instead call the function to load the data once on component mount (useEffect). You'll also need to replace useState('') with useState([]) since you're trying to map over this data when rendering.
const NormalCarousel = (props) => {
const { api } = props;
const navigation = useNavigation();
const [responseData, setResponseData] = useState([]);
useEffect(() => {
getAPI();
}, []);
async function getAPI() {
const API = await axios.get(api).catch((error) => alert(error));
setResponseData(API);
}
return(
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{
responseData.map(item => (
<TouchableOpacity style={styles.CarouselTouchable} onPress={() => navigation.navigate("Ürün", {id: item.item_id})}>
<Image
style={styles.CarouselImage}
source={{uri: item.item_avatar}}
/>
<View style={styles.CarouselView}>
<Text style={styles.CarouselTitle}>{item.item_name}</Text>
<Text style={styles.CarouselSubtitle}>{item.item_stock_code}</Text>
</View>
</TouchableOpacity>
))
}
</ScrollView>
)
}

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 (