I have this code for React Native:
componentWillMount() {
shiftKeys = []; // need to clear previously set data or we will get dupicate array key errors
// get the current user from firebase
const userData = firebaseApp.auth().currentUser;
const profileRef = firebaseApp.database().ref('userdata').child(userData.uid);
profileRef.on('value', snapshot => {
if (snapshot.val().hasOwnProperty('licenseType') && snapshot.val().hasOwnProperty('licenseState') && snapshot.val().hasOwnProperty('distance')) {
this.setState({
licenseType: snapshot.val().licenseType,
licenseState: snapshot.val().licenseState,
distancePref: snapshot.val().distance,
});
console.log('State1', this.state.distancePref)
} else {
// redirect back to profile screens because we need three values above to search.
this.props.navigation.navigate('Onboarding1')
}
});
}
componentDidMount() {
console.log('State2', this.state.distancePref)
var geoQuery = geoFire.query({
center: [45.616422, -122.580453],
radius: 1000// need to set dynamically
});
I think this is some kind of scope issue?
When I look at the console log, State 1 is set correctly, but State 2 prints nothing.
In my app I need to look up a users distance preference, then use that to run a query.
How do I pass the value from componentWillMount to componentDidMount?
https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle
setState in componentWillMount - bad way. You do not solve the problem this way, because state will not be updated until componentDidMount (see lifecycle). Check your condition when creating the state, in the constructor.
Or you can solve the problem using redux.
The root issue with this problem had to do with my not understanding how react and react native render the code.
I was trying to get users info from firebase, then set preferences, then use those preferences to run a search.
I added redux and thunk to handle the getting and saving of the users preferences separately from (and before) the user has a chance to search.
Related
I'm creating an app which passes some crucial info via AsyncStorage, but now have a problem when updating it on another screen.... Let's see:
On Screen 1 :
Load data from AsyncStorage on componentDidMount
componentDidMount() {
AsyncStorage.getItem("userChats").then((value) => {
this.setState({userChats: JSON.parse(value)});
});
}
then on Screen 2 I modify userChats.....
I'll like that when coming back again to Screen 1, the changes made on Screen 2 be reflected on Screen 1 but there are NOT as componentDidMount is not trigged again...........
What's the correct way to do it?
Thanks
componentDidMount is a life-cycle method. Navigating from Screen1 to Screen2 does NOT unmount Screen1. So, when you come back from Screen 2 to Screen 1, the Screen 1 does not mounting because it was NOT unmounted. Hence, componentDidMount is not called.
Whats's the correct way of doing this?
You should use Context API. When you load from AsyncStorage, set that value to Context as well. When you update the value, write changes to both AsyncStorage and Context.
PS: The AsyncStorage may not needed. It depends on your requirement. Most probably, you will be able to achieve this only with Context API.
Please check the following snack. It is done using hooks. You can do the same using class components.
https://snack.expo.io/3L9QSqWqt
UPDATE:
If the data to be handled is too large, it is not recommended to use Context since it saves all the data in the device RAM and consuming too much RAM may result in app crash.
To do this without using context:
(1) Define a function to retrieve data from AsyncStorage.
loadData() {
AsyncStorage.getItem("userChats").then((value) => {
this.setState({userChats: JSON.parse(value)});
});
}
(2) Call it in componentDidMount.
componentDidMount() {
this.loadData()
}
(3) When navigating to the Screen2, pass a callback function as a prop to call loadData function.
this.props.navigation.navigate('Screen2', {
onGoBack: () => this.loadData(),
});
(4) Then in the Screen2, before goBack, you can do this:
await AsyncStorage.setItem('userChats', updatedData);
this.props.navigation.state.params.onGoBack();
this.props.navigation.goBack();
Then, the loadData function is called in the screen2.
PS: Since you use state to store the retrieved data from the AsyncStorage, you can also directly load them into the Context and use. But, keep in mind that, using too much of RAM may cause app crash.
i have two screens one where the profile information is showing and another screen to edit the information. If i entered the first screen profile it's shows me the right data from the database. Then i move to the next screen where i can change the Information everthing worked so far. But if I go back to the previous screen i still see the old data. So there is no rerendering.
But if i navigate to the other screen that screen fetched the new data. and the call getCurrentUserProfile is executed
This ist the screen with the profile information about the user.
const ProfileScreen = props => {
const [userObj, setUserObj] = useState({});
useEffect(() => {
let mounted = true;
getCurrentUserProfile().then(user => {
if (mounted) {
setUserObj(user);
}
console.log(user)
});
return () => mounted = false;
}, []);
console.log("----b------") // This is only output on the first call
}
How can i fix this. Is there a way when in the database is something changed, so the component rerender and fetches the new data.
Thanks.
You are probably using #react-navigation package. So, if you want to refetch data when come back to the previous page, you can use https://reactnavigation.org/docs/use-is-focused on that “previous” page.
Just look at their docs, you will get the idea.
P.S: Usually we don't unmount components directly. In React Native, we use navigator to mount/unmount components
I am new to React Native
I am trying to create a multiselect view where user can select and deselect the items and then the selected items should pass back to the previous container and when user comes back to the next view the selected items should be checked.
I am trying to implement but getting the issue it is not updating data accurately. It shows only 1 selected item when I came back again to the screen.
Can anyone tell me the best way to do that or if there is any tutorial.
Should I do it with Redux or using react native?
Any help would be appreciated!!
Thanks!!
I believe the issue you describe is due to the following:
In componentDidMount you are calling updateItemWithSelected in a loop. This updateItemWithSelected call is both overwriting the checked attributes for all of the arrayHolder values on each call and also not using the updater function version of setState, so the later call of the loop may overwrite the earlier calls since setState is async and batched. If you are not using updateItemWithSelected elsewhere you should simplify componentDidMount to:
componentDidMount() {
const selectedTitles = {};
const { state } = this.props.navigation
const params = state.params || {};
if (params.data.length){
params.data.forEach((element) => {
// create a map of selected titles
selectedTitles[element.title] = true;
})
}
const arrayHolder = this.array.map(item => {
// map over `this.array` and set `checked` if title is in `selectedTitles`
return {...item, checked: !!selectedTitles[item.title]};
});
this.setState({ arrayHolder });
}
and delete updateItemWithSelected.
I am using React-navigation to handle deep link.
Let's say I am in BusinessProfile Page that is currently displaying detail for BUSINESS B1. I click on home button and minimize my app. When I click on a deep link, myapp://BusinessProfilePage/B2, It takes me to the BusinessProfile Page but still displays result for Business B1. The function to get business detail for B2 is not called.
How can I make the page refresh when a page opens from a deep link.
P.S. I cannot call the function in componentDidUpdate because when the function to get Business Detail is called, it updates the state which then evoke componentDidMount again.
For v5 Use following prop which is alternate to 'key' option in navigate.
getId={({ params }) => params.id}
In this case id will be different. In your case it will be 'B1' and 'B2'. This will create multiple instance of same screen.
You should call your function in a listener for the change event of AppState:
import { AppState } from 'react-native';
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextAppState) => {
if (nextAppState === 'active') { // App has come to the foreground
if(this.state.currentBusiness.ID != (ID received in deep link)) // Need to get data
this.getBusiness(ID received in deep link);
}
};
Taking my best guess here with regards to variable names as you didn't provide any code (you should always include code samples when describing your issue :) ), but you get the idea.
I have a FlatList in ReactNative which pulls a list of articles from an API. When the end of the list is reached on scrolling, a further page of articles is pulled from the API and appended to the articles list in a Redux reducer.
The FlatList is set up as:
render() {
return(
<FlatList data={this.props.articles.articles} // The initial articles list via Redux state
renderItem={this.renderArticleListItem} // Just renders the list item.
onEndReached={this.pageArticles.bind(this)} // Simply calls a Redux Action Creator/API
onEndReachedThreshold={0}
keyExtractor={item => item.id.toString()}/>
)};
The Redux 'articles' state object is mapped to the component using the React Redux connect function. The relevant reducer (some items removed for brevity) looks like:
/// Initial 'articles' state object
const INITIAL_STATE = {
articles: [],
loading: false,
error: '',
pageIndex: 0,
pageSize: 10
};
export default(state = INITIAL_STATE, action) => {
switch(action.type){
// The relevant part for returning articles from the API
case ARTICLES_SUCCESS:
return { ...state, loading: false, articles: state.articles.concat(action.payload.items),
pageIndex: action.payload.pageIndex, pageSize: action.payload.pageSize}
default:
return state;
}
}
The payload is a simple object - the articles are contained in the action.payload.items array, and there is additional paging information as well in the payload (not important to this problem).
Everything is fine with the API returning the correct data.
The problem is that when the end of the FlatList is reached on scrolling, the API is called, the new articles are pulled fine and appended to the this.props.articles state object and to the FlatList, BUT the FlatList jumps/scrolls back to the first item in the list.
I think the problem is probably with how I am returning the state in the Redux reducer but I'm not sure why.
Is using concat the correct way to return the new state with the new articles appended?
It might be too late to answer this but I experienced exact same problem having my data served from redux to the component and managed to fix the jumping effect by making my component a normal Component rather than PureComponent allowing me to use shouldComponentUpdate life-cycle hook method. This how the method should look like for you component:
shouldComponentUpdate(nextProps) {
if (this.props.articles === nextProps.articles) {
return false;
}
return true;
}
This method (if implemented) should return a boolean value indicating whether the component should update or not. What i did here was to prevent the component from re-rendering in case the contents of articles hasn't changed.
Hope this would be helpful for you or others who might have faced similar problem.
You need to add to listItem styles height and width. Freezing and jumping maybe because list tried to calculate sizes for all items even if they dont display.
And you can find answer in: https://github.com/facebook/react-native/issues/13727
Using FlatList as component and adding data as props re-renders whole content, so it scrolls to top. I recommend using it directly inside your page and changing its data using this.state, not props