Reset Select Item - React Native - react-native

I have a form which includes a select dropdown (items are populated via an api call). When I leave the screen I would like to be able to reset this back to it's initial state (Default state is a placeholder - Select Event)
I can clear text and textarea inputs within a useFocusEffect() but struggling with understanding how to reset a select dropdown
To reset the select dropdown i have tried setEventTypeData([]); but when navigating back to the screen, the last selected option is still selected (text inputs have been cleared though)
export const CreateNewEvent = ({navigation}) => {
const globalContext = useContext(AppContext);
const userId = globalContext.userInfo.id;
// dropdown populated with this
const [eventTypeData, setEventTypeData] = useState([]);
const [newEventDescription, setEventDescription] = useState('');
const [newEventLimit, setEventLimit] = useState(0);
const clearFormData = () => {
setEventTypeData([]); // tried setting back to original state but does not work
setEventDescription('');
setEventLimit(0);
};
useFocusEffect(
React.useCallback(() => {
const body = JSON.stringify({userId});
fetch(eventTypesUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: body,
})
.then(response => response.json())
.then(json => setEventTypeData(json))
.catch(error => console.error(error))
.finally(() => setLoading(false));
return () => {
// Run logic when the user leaves screen,
// Clear form
clearFormData();
};
}, [userId]),
);
// Select Dropdown
{/* Event Name Select Field */}
<FormControl isRequired isInvalid={'eventName' in errors}>
<FormControl.Label>Select Event</FormControl.Label>
<Select
onValueChange={newEventName =>
updateEventNameAndDescription(newEventName)
}
placeholder="Select Event"
{eventTypeData.map(event => (
<Select.Item
key={event.id}
label={event.name}
value={event.name}
/>
))}
</Select>
}
How can i ensure that when navigating back to this screen that the Select dropdown is reset to its original state
Thanks

I rewrite your example. I hope this help. You forget to unsubscribe
from API call
import { useIsFocused } from '#react-navigation/native';
const isFocused = useIsFocused();
useEffect(() => {
if (!isFocused) {
clearFormData()
}
}, [isFocused]);
useFocusEffect(
React.useCallback(() => {
const body = JSON.stringify({ userId });
const unsubscribe = fetch(eventTypesUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: body,
})
.then((response) => response.json())
.then((json) => setEventTypeData(json))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
return () => unsubscribe();
};
}, [userId]),
);

export const CreateNewEvent = ({navigation}) => {
const globalContext = useContext(AppContext);
const userId = globalContext.userInfo.id;
// dropdown populated with this
const [eventTypeData, setEventTypeData] = useState([]);
const [newEventDescription, setEventDescription] = useState('');
const [newEventLimit, setEventLimit] = useState(0);
const [selectedEventName, setSelectedEventName] = useState();
const clearFormData = () => {
setSelectedEventName();
setEventDescription('');
setEventLimit(0);
};
useEffect(() => {
selectedEventName ? updateEventNameAndDescription(selectedEventName) : clearFormData();
}, [selectedEventName])
useFocusEffect(
React.useCallback(() => {
const body = JSON.stringify({userId});
fetch(eventTypesUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: body,
})
.then(response => response.json())
.then(json => setEventTypeData(json))
.catch(error => console.error(error))
.finally(() => setLoading(false));
return () => {
// Run logic when the user leaves screen,
// Clear form
clearFormData();
};
}, [userId]),
);
// Select Dropdown
{/* Event Name Select Field */}
<FormControl isRequired isInvalid={'eventName' in errors}>
<FormControl.Label>Select Event</FormControl.Label>
<Select
value={selectedEventName}
onValueChange={newEventName =>
setSelectedEventName(newEventName)
}
placeholder="Select Event"
{eventTypeData.map(event => (
<Select.Item
key={event.id}
label={event.name}
value={event.name}
/>
))}
</Select>
}

If you're using React Native Picker or something related, That picker is bound to the device native Select component, This has more performance benefit as it's not run on JavaScript thread, React rerendering will not affect that component.
But in this situation, we need to force this component to unmount when the user leaves the screen or mount when the screen is focused.
// Top-level import
import { useIsFocused } from '#react-navigation/native';
// Inside functional component
const isFocused = useIsFocused();
// Force <Select> to unmount or mount when screen focused
{ isFocused && <Select
value={selectedEventName}
onValueChange={newEventName =>
setSelectedEventName(newEventName)
}
placeholder="Select Event"
{eventTypeData.map(event => (
<Select.Item
key={event.id}
label={event.name}
value={event.name}
/>
))}
</Select>}

Related

why passing params do not refresh and have to refresh the new page update

i am trying to pass params to next page by calling id from previous
page. but when you enter the next page the data does not appear
immediately and you have to refresh the page which takes quite a long
time, and faithfully calling new data, what appears is the previous
data, you have to refresh the page to bring up new data. what is the
solution?
first page code
...//
onPress={() => {
setId(item.ID);
navigation.navigate('Rekap_Bencana', {
params: id
});
}}
...//
const [dataBencana, setDataBencana] = useState();
const [id, setId] = useState();
useEffect(() => {
getData();
}, []);
const getData = () => {
fetch('http://192.168.0.103/aplikasi/restapi.php?op=getDatabencana')
.then(response => response.json())
.then(json => {
// console.log(json);
setDataBencana(json);
// console.log(dataBencana);
});
};
params page code
const Rekap_Bencana = () => {
const route = useRoute();
const navigation = useNavigation();
const {params} = route.params;
useEffect(() => {
getData();
console.log(params);
}, []);
const [data, setData] = useState();
const getData = () => {
fetch('http://192.168.0.103/aplikasi/restapi.php?op=getBencanaDetail', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: params,
}),
})
.then(res => res.json())
.then(resp => {
setData(resp);
console.log(resp);
});
};

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop React Native with FlatList render

I don't understand why I get this error, when rendering screen I use useSelector to get state from store(I'm using redux) I get data from api and pass it to flatList to render the list, everything is normal but I don't know why
const HistoryScreen = () => {
const { loading, histories } = useSelector((state) => state.historiesList)
useEffect(() => {
if (user) {
dispatch(listHistory())
}
}, [dispatch, user])
return (
<FlatList data={histories} renderItem={({ item, i }) => <HistoryCard key={i} onPress={() => console.warn('cliecked')} post={item} ></HistoryCard>}>
</FlatList >
</View >}</>
)
}
export default HistoryScreen
action:
export const listHistory = (skip = 0, limit = 10) => async (dispatch, getState) => {
try {
dispatch({ type: HISTORY_LIST_REQUEST })
const user = await AsyncStorage.getItem('userInfo')
const userInfo = user ? JSON.parse(user) : null
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`
}
}
const payload = { skip: skip, limit: limit }
const { data } = await axios.post(`${api}/histories/`, payload, config)
dispatch({ type: HISTORY_LIST_SUCCESS, payload: data.data })
console.log(data.data)
} catch (error) {
dispatch({ type: HISTORY_LIST_FAILED, payload: error })
}
}

Fecth JSON URL in React Native

I am trying to obtain the data from this website: https://reactnative.dev/movies.json
I use the following code:
fetch("https://reactnative.dev/movies.json")
.then(response => response.json())
.then((responseJson) => {
setTimeout(() => {
this.setState({
loading: false,
dataSource: responseJson
})
}, 2000)
Alert.alert(responseJson)
})
.catch(error => console.log(error))
return (
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} tintColor={themes[theme].auxiliaryText} />
}
/>
);
};
My problem is that Alert.alert (responseJson) throws the following error:
This error
... and I would like to get the data for the flatlist.
Who helps me, please?
UPDATE:
...
constructor(props) {
super(props);
this.state = {
moviesList: '',
};
}
...
renderScroll = () => {
const { moviesList } = this.state;
fetch("https://reactnative.dev/movies.json")
.then(response => response.json())
.then((responseJson) => {
setTimeout(() => {
this.setState({
loading: false,
moviesList: responseJson.movies
})
Alert.alert(moviesList)
}, 2000)
})
.catch(error => console.log(error))
...
I would like Alert.alert to receive the data. That is to say, get the data out of the fetch function.
If inside the Alert.alert I put responseJson.movies if the data appears, but if I put moviesList it won't
If I post the following in the browser console it sends me an undefined error. I just want to receive the data from outside the function.
fetch("https://reactnative.dev/movies.json")
.then(response => response.json())
.then((responseJson) => {
setTimeout(() => {
this.setState({
loading: false,
moviesList: responseJson.movies
})
}, 2000)
})
.catch(error => console.log(error))
alert(this.moviesList)
You must print your response data by console.log and make sure what the response data is.
According to your error, I think you are getting Object type data after fetching, but you need to assign String into Alert.alert, therefore I suggest you try the following.
Alert.alert(responseJson.title)
I just tested this code it seems to be working. Is that what you are looking for?
with currElement you have access to the filmed object.
import { useEffect, useState } from "react"
function Home() {
const [isLoading, setLoading] = useState(true)
const [data, saveData] = useState([])
useEffect(() => {
fetch("https://reactnative.dev/movies.json")
.then(response => response.json())
.then(responseJson => {
console.log(responseJson.movies)
saveData(responseJson.movies)
setLoading(false)
})
.catch(error => console.log(error))
}, [])
console.log(data)
return isLoading ? (
<p> i am loading</p>
) : (
<ul>
{data.map(currElement => (
<li key={currElement.id}> {currElement.title}</li>
))}
</ul>
)
}

React Router: Check that JWT is valid before rendering a Private Route

I'm looking to implement a real world authentication for a React Router app. Every tutorial I've seen uses fakeAuth to simulate authentication, but doesn't actually implement real world authentication. I'm trying to actually implement authentication. Is this possible?
Right now I'm sending a jwt to the back end to check whether it is valid before returning the Component I want to render -- Redirect to Login if jwt authentication fails, or render Dashboard if it's a valid jwt. The problem is the ProtectedRoute is returning the redirect to /login before the back end is returning whether the jwt is valid or not.
How can I get real world authentication in my React-Router app? Is this even possible?
const PrivateRoute = ({ component: Component, ...rest }) => {
const [auth, setAuth] = useState(false);
useEffect(() => {}, [auth])
useEffect(() => {
// send jwt to API to see if it's valid
let token = localStorage.getItem("token");
if (token) {
fetch("/protected", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
})
.then((res) => {
return res.json()
})
.then((json) => {
if (json.success) {
setAuth(true);
}
})
.catch((err) => {
setAuth(false);
localStorage.removeItem("token");
});
}
}, [])
return (<Route {...rest}
render={(props) => {
return auth ? <Component {...props} /> : <Redirect to="/login" />
}} />)
}
}
I would say you need a state between authenticated/not valid jwt. I would use another state field, isTokenValidated (or isLoading):
const PrivateRoute = ({ component: Component, ...rest }) => {
const [auth, setAuth] = useState(false);
const [isTokenValidated, setIsTokenValidated] = useState(false);
useEffect(() => {
// send jwt to API to see if it's valid
let token = localStorage.getItem("token");
if (token) {
fetch("/protected", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
})
.then((res) => {
return res.json()
})
.then((json) => {
if (json.success) {
setAuth(true);
}
})
.catch((err) => {
setAuth(false);
localStorage.removeItem("token");
})
.then(() => setIsTokenValidated(true));
} else {
setIsTokenValidated(true); // in case there is no token
}
}, [])
if (!isTokenValidated) return <div />; // or some kind of loading animation
return (<Route {...rest}
render={(props) => {
return auth ? <Component {...props} /> : <Redirect to="/login" />
}} />)
}
}
As an alternative way, your backend server.js file can check if jwt is valid or not and send a status code accordingly. Then your frontend react component can check this status code and render (or not render) page accordingly.
For component:
import {useState, useEffect} from "react";
const Private = () => {
const [statusAuth, setStatusAuth] = useState(false);
useEffect(() => {
fetch("/privateroute")
.then((res) => {
if (res.status === 200) {
setStatusAuth(true)
} else {
setStatusAuth(false)
}
});
}, []);
return(
{statusAuth && <div>rendered page</div>}
{!statusAuth && <div>You need to login. <Link to="/login"><span>Click for login page</span></Link></div>}
)
}
For server.js file:
app.get("/privateroute", function(req, res){
const token = req.cookies.jwt;
if (token) {
jwt.verify(token, "signature-of-your-jwt-token", () => {
res.status(200).end();
})
} else {
res.status(404)
res.send("Tokens didnt match");
}
});

fetching values from server for multiselect picker react native

I tried fetching values from server for multi select picker component from the package https://github.com/toystars/react-native-multiple-select. But i get an error message: TypeError: null is not an object(evaluating this.state.LangKnown).
Please Kindly help.Thank u
My JSON values
{
"MFBasic": {
"SkinTones": "DARK,FAIR,VFAIR",
"Build": "SLIM,ATHLETIC,PLUMPY",
"Gender": "F,M,T",
"Genre": "ACTION,COMEDY,DRAMA",
"Languages": "ENG,HINDI,TAM",
"MediaModes": "ADS,MOVIES,SHORTFILMS",
"Tags": "BIKES,HOME,JEWELLARY"
},
"Result": "Successfully Loaded MF Basic Details",
"Code": 100
}
import React, {Component} from "react";
import { Text, View, StyleSheet, Picker, Alert } from "react-native";
import MultiSelect from "react-native-multiple-select";
export default class App extends React.Component {
state = {
LangPickerValueHolder: [],
LangKnown: []
}
componentDidMount () {
fetch('https://movieworld.sramaswamy.com/GetMFBasicDetails.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(response => response.json())
.then(responseJson => {
let langString = responseJson.MFBasic.Languages;
let langArray = langString.split(',');
this.setState({
LangPickerValueHolder: langArray
});
}).catch(error => {
console.error(error);
});
}
render () {
return (
<View style={styles.itemContainer}>
{<MultiSelect
ref={(component) => { this.multiSelect = component; }}
onSelectedItemsChange={(value) =>
this.setState({ LangKnown: value })
}
selectedItems={this.state.LangKnown}
onChangeInput={ (text) => console.log(text)}
displayKey = ''name
submitButtonText="Submit">
{this.state.LangPickerValueHolder.map((item, key) => (
<MultiSelect.Item item = {item} uniqueKey = {key}/>
))}
</MultiSelect>}
</View>
);
}
}
You've made a good attempt at how to set up the MultiSelect however there are a couple of issues that need to be resolved.
If you look at the dependency the data that should be passed to it should be an array of objects. The example gives the object as { id: '92iijs7yta', name: 'Ondo' } We can easily transform your data from an array of strings into an array of objects that match the example.
let LangPickerValueHolder = langArray.map((name, id) => { return { name, id }; });
Using a map we can convert the array.
This would make your componentDidMount look like the following:
componentDidMount () {
fetch('https://movieworld.sramaswamy.com/GetMFBasicDetails.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(response => response.json())
.then(responseJson => {
let langString = responseJson.MFBasic.Languages;
let langArray = langString.split(',');
let LangPickerValueHolder = langArray.map((name, id) => { return { name, id }; }); // <- here we had the mapping function
this.setState({ LangPickerValueHolder }); // <- save the new array of objects into the state
console.log(langArray);
}).catch(error => {
console.error(error);
});
}
Setting up the MultiSelect component requires a few more changes.
Firstly there is no MultiSelect.Item so the map that you are using to populate the MultiSelect won't work. Instead you need to use the items prop to set the items. Next you need to tell the MultiSelect component the correct uniqueKey prop (which in our case will be id) and set the displayKey correctly.
Here is what your render could look like.
render () {
return (
<View style={styles.container}>
<MultiSelect
ref={(component) => { this.multiSelect = component; }}
onSelectedItemsChange={(value) =>
this.setState({ LangKnown: value })
}
uniqueKey="id"
items={this.state.LangPickerValueHolder}
selectedItems={this.state.LangKnown}
onChangeInput={ (text) => console.log(text)}
displayKey = 'name'
submitButtonText="Submit" />
</View>
);
}
Here is it put together in a snack: https://snack.expo.io/#andypandy/multiselect-with-data-from-api
Here is the code from the snack:
import React from 'react';
import { View, StyleSheet } from 'react-native';
import MultiSelect from 'react-native-multiple-select';
export default class App extends React.Component {
// declaring state like this is absolutely fine, it doesn't need to be in a constructor
state = {
LangPickerValueHolder: [],
LangKnown: []
}
componentDidMount () {
fetch('https://movieworld.sramaswamy.com/GetMFBasicDetails.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(response => response.json())
.then(responseJson => {
let langString = responseJson.MFBasic.Languages;
let langArray = langString.split(',');
let LangPickerValueHolder = langArray.map((name, id) => { return { name, id }; });
this.setState({
LangPickerValueHolder
});
console.log(langArray);
}).catch(error => {
console.error(error);
});
}
render () {
return (
<View style={styles.container}>
<MultiSelect
ref={(component) => { this.multiSelect = component; }}
onSelectedItemsChange={(value) =>
this.setState({ LangKnown: value })
}
uniqueKey="id" // <- set the value for the uniqueKey
items={this.state.LangPickerValueHolder} // <- set the items you wish to show
selectedItems={this.state.LangKnown}
onChangeInput={ (text) => console.log(text)}
displayKey = 'name' . // <- fix typo here
submitButtonText="Submit" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'white',
padding: 8
}
});