Setting state with a function in another file - react-native

Without attempting to update my state, the initial location in state is presented correctly. When I set state using a helper function, nothing is displayed in my app. What am I doing wrong? Additionally, logging props inside ShowLocation's render() shows that the coords{lat:xx,long:xx} are coming through correctly.
App.js
import * as helpers from './src/helpers';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = globals.initial_state;
}
componentDidMount(){
this.setState({location:helpers.getLocation()});
}
render() {
return (
<View>
<ShowLocation coords={this.state.location} />
</View>
);
}
}
ShowLocation.js
class ShowLocation extends Component {
constructor(props){
super(props);
}
render(){
return(
<View>
<Text>{this.props.coords.lat}, {this.props.coords.long}</Text>
</View>
)
}
};
helpers.getLocation:
export function getLocation(){
coords = {};
navigator.geolocation.getCurrentPosition(
(position) => {
coords['lat'] = position.coords.latitude
coords['long'] = position.coords.longitude
},
(error) => this.setState({ navigatorError: error.message }),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 },
);
return coords;
}

Did you tried:
componentDidMount(){
this.setState({ location: getLocation().bind(this) });
}
Or, same thing, but cleaner code:
constructor() {
// other stuff
this.getLocation = getLocation().bind(this)
}
componentDidMount(){
this.setState({ location: this.getLocation() });
}
Edit:
You must import { getLocation} from 'path/of/file'

Related

React Native: Getting data from Firebase

I'm simply trying to retrieve data from the database in Firebase, and here's what I've got
var userList = [];
firebase.database()
.ref('/users/')
.once('value')
.then(snapshot => {
snapshot.forEach((doc) => {
userList.push(doc.val());
});
});
console.log(userList);
Even though I copy and pasted this code from a tutorial, the userList is empty outside of the snapshot. Can you tell me why that is?
The request to firebase is asynchronous so console.log(userList); is called before userList.push(doc.val()); gets called.
You should make userList a component state variable so that when you update it your component will re render.
Something like the following should work:
class UserListComponent extends Component {
constructor(props) {
super(props);
this.state = {
userList: [],
};
}
componentDidMount() {
this.getUsers();
}
getUsers() {
firebase
.database()
.ref('/users/')
.once('value')
.then((snapshot) => {
snapshot.forEach((doc) => {
this.setState({
userList: [...this.state.userList, doc.val()],
});
});
});
}
render() {
return (
<View>
{this.state.userList.map((item) => {
return (
<View>
<Text>{item.name}</Text>
</View>
);
})}
</View>
);
}
}

React Native : How to get device Screen Brightness and render it

I`m creating an React App to display device Info. I want to render Screen Brightness level, not in Console. How do I do it?
DeviceBrightness.getSystemBrightnessLevel().then(function(luminous) {
console.log(luminous)
})
I expected to render the screen brightness level, not to display in console
import DeviceBrightness from 'react-native-device-brightness';
export default class App extends Component{
constructor(props){
super(props);
this.state = {
isLoaded: false,
brightness: 0,
};
}
componentWillMount() {
DeviceBrightness.getSystemBrightnessLevel()
.then((luminous) =>{
this.setState({
brightness: luminous,
isLoaded: true,
});
});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.instructions}>{this.state.brightness}</Text>
</View>
);
}
}
import DeviceBrightness from 'react-native-device-brightness';
export default class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoaded: false,
brightness: 0
};
}
componentDidMount() {
DeviceBrightness.getSystemBrightnessLevel()
.then(luminous => {
this.setState({
brightness: luminous,
isLoaded: true,
});
});
}
render() {
const { isLoaded, brightness } = this.state;
if (!isLoaded) {
return {/*loading view*/}
} else {
return (
<Text>{brightness}</Text>
);
}
}
}

Multiple Components with same state name, how to change one state of one component without affecting the others?

In my react native program I have component (Bell) that is called for every item/list called from an API. The bell is used to make a notification (and cancel it)on press and I made a state (isBellActive) to determine if the Bell has been toggled on or not. If it is, isBellActive is true and false otherwise. Im storing the data via asyncStorage.
The problem im having is that if I change the state of one bell(on one item/list) and then close the app and relaunch, that state change from that bell will affect all the other bell component's isBellActive state. How to make it so the state (even though they all share the same name) are kept for one specific item/list
Bell Component Class
export default class Bell extends React.Component {
constructor(props) {
super(props);
this.state = {
isBellActive: null,
};
}
componentDidMount = ()=>{
AsyncStorage.getItem('isBellActive').then(value => this.setState({ isBellActive: JSON.parse(value) }));
}
setTrue(){
AsyncStorage.setItem('isBellActive', JSON.stringify(true)).then(() => {
this.setState({ isBellActive: true});
});
}
setFalse(){
AsyncStorage.setItem('isBellActive', JSON.stringify(false)).then(() => {
this.setState({ isBellActive: false});
});
}
render() {
return (
<Ionicons
name={this.state.isBellActive? "md-notifications":"md-notifications-off"}
color={"white"}
size={30}
style={styles.NotifIcon}
onPress={() => {
Vibration.vibrate()
if(this.state.isBellActive == false){
PushNotificationIOS.scheduleLocalNotification({
userInfo:{
ID: this.state.ID
},
alertTitle: "Launching Soon:",
alertBody: this.state.alertBody,
fireDate: this.state.fireDate // in 30 mins
});
this.setTrue()
this.setState({Key:true})
}
else if(this.state.isBellActive != false){
PushNotificationIOS.cancelLocalNotifications({ID:this.state.ID});
this.setFalse()
}
}
}
}}
/>
);
}
}
Class that calls Component
export default class LaunchingScreen extends React.Component{
let launches = this.state.dataSource.map((item, key) => {
..
<View>
..
<Bell />
..
</View>
..
}
}
UPDATE
This is in class that calls components, it gets a JSON that has all the info:
componentDidMount(){
return fetch("https://launchlibrary.net/1.4/launch/next/20")
.then(response => response.json())
.then(responseJson => {
this.setState({
isLoading: false,
dataSource: responseJson.launches
});
})
.catch(error => {
console.log(error);
});
}
As per the response from the API shared by you, it looks like that id property is unique and you can use that property to uniquely define key for Bell component and used that key to store/retrieve data from AsyncStorage. Please consider following code snippets
Change LaunchingScreen to add key={item.id}
export default class LaunchingScreen extends React.Component{
let launches = this.state.dataSource.map((item, key) => {
..
<View>
..
<Bell key={item.id}/>
..
</View>
..
}
}
Now in the Bell component, use the key property to access data from AsyncStorage
export default class Bell extends React.Component {
constructor(props) {
super(props);
this.state = {
isBellActive: null
};
this.accessKey = `${props.key}-isBellActive`;
}
componentDidMount = ()=>{
AsyncStorage.getItem(this.accessKey).then(value => this.setState({ isBellActive: JSON.parse(value) }));
}
setTrue(){
AsyncStorage.setItem(this.accessKey, JSON.stringify(true)).then(() => {
this.setState({ isBellActive: true});
});
}
setFalse(){
AsyncStorage.setItem(this.accessKey, JSON.stringify(false)).then(() => {
this.setState({ isBellActive: false});
});
}
render() {
return (
<Ionicons
name={this.state.isBellActive? "md-notifications":"md-notifications-off"}
color={"white"}
size={30}
style={styles.NotifIcon}
onPress={() => {
Vibration.vibrate()
if(this.state.isBellActive == false){
PushNotificationIOS.scheduleLocalNotification({
userInfo:{
ID: this.state.ID
},
alertTitle: "Launching Soon:",
alertBody: this.state.alertBody,
fireDate: this.state.fireDate // in 30 mins
});
this.setTrue()
this.setState({Key:true})
}
else if(this.state.isBellActive != false){
PushNotificationIOS.cancelLocalNotifications({ID:this.state.ID});
this.setFalse()
}
}
}
}}
/>
);
}
}
In the above component, we are first deciding the access key using this.accessKey = ${props.key}-isBellActive; and then using this.accessKey instead of isBellActive.
Hope this will help!!!
If you are using the same component multiple times and want to have a different state for each of them then you have to set key property for each component.
Keys help React identify which items have changed, are added, or are removed.
For this kind of situation where you want to use the same component multiple times, you have to set key property. Otherwise, each component will have the same state.

I want to use scrollTo

I work on a project in React Native and I would like to set my ScrollView position. So I search and I found we should do this with scrollTo but I have an error:
TypeError: Cannot read property 'scrollTo' of undefined
My code:
export default class Index_calendar extends Component {
componentDidMount() {
const _scrollView = this.scrollView;
_scrollView.scrollTo({x: 100});
}
render() {
return (
<ScrollView ref={scrollView => this.scrollView = scrollView}>
{this.renderCalandar()}
</ScrollView>
);
}
}
You can use the InteractionManager to solve this issue.
For instance
InteractionManager.runAfterInteractions(() => this.scroll.current.scrollTo({ x }));
Why not just scrollTo in the render method?
export default class Index_calendar extends Component {
constructor(props) {
super(props);
this.scrollView = null;
}
render() {
return (
<ScrollView ref={scrollView => {
//Sometimes ref can be null so we check it.
if(scrollView !== null && this.scrollView !== scrollView){
this.scrollView = scrollView
scrollView.scrollTo({x: 100});
}}>
{this.renderCalandar()}
</ScrollView>
);
}
}
I found the solution ! we need to use setTimeout like that :
setTimeout(() => {
this.scrollView.scrollTo({x: 100});
}, 1);
You seem to make correct reference. But I suggest to init the reference and make it less error prone:
export default class Index_calendar extends Component {
constructor(props) {
super(props);
this.scrollView = null;
}
componentDidMount() {
const _scrollView = this.scrollView;
if (_scrollView) {
_scrollView.scrollTo({x: 100});
}
}
InteractionManager.runAfterInteractions(() => {
this.scrollRef.scrollTo({
x: 0,
y: 0,
animated: true,
});
});
This worked for me to reset the scroll to top

Update state when user press back button in React Native

I use react-navigation for manage routes. This is my Home component:
class HomeScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: {
firstname: 'John',
avatar: 'john-profile.png',
location: 'Canada',
}
}
}
componentDidMount() {
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
render() {
return (
<View>
<Image src="{this.state.userProfile.avatar}" />
<Text>Firstname: {this.state.userProfile.firstname}</Text>
<Text>Location: {this.state.userProfile.location}</Text>
</View>
);
}
}
And this is the Profile screen:
class ProfileScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: null,
}
}
componentDidMount() {
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
save() {
var userSavedProfile = this.state.userProfile;
userSavedProfile.firstname = "Peter";
userSavedProfile.avatar = "peter-avatar.png";
userSavedProfile.location = "EEUU";
this.setState({userProfile: userSavedProfile});
AsyncStorage.setItem('userProfile', JSON.stringify(this.state.userProfile), () => {});
}
render() {
return (
<View>
<Button title="Save" onPress={() => this.save()} />
</View>
);
}
}
When I save the new user information and I press back button in header (react-navigation) the user profile is old, firstname = John, etc... How update state from Home when user press back button and refresh data?
You can use BackHandler from react-native
https://facebook.github.io/react-native/docs/backhandler.html
You can change state inside function of backhandler
I think that your application would need a state manager, where you could store your user information and access it anywhere in the app. You should take a look at Redux. It would fit your needs and the info in your Home screen would automatically update.
but for anyone who will need this functionality in there react native application here is the solution you can try.
using react navigation.
import {withNavigationFocus} from "react-navigation";
class Profile extends Component {
...
}
export default withNavigationFocus(Profile);
There can be two workarounds check it out -
1 Send callback in params
class HomeScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: {
firstname: 'John',
avatar: 'john-profile.png',
location: 'Canada',
}
}
this.getUserData = this.getUserData.bind(this);
}
componentDidMount() {
this.getUserData;
}
getUserData = () =>{
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
render() {
return (
<View>
<Image src="{this.state.userProfile.avatar}" />
<Text onPress={()=>this.props.navigation.navigate('ProfileScreen', this.getUserData)}>Firstname: {this.state.userProfile.firstname}</Text>
<Text>Location: {this.state.userProfile.location}</Text>
</View>
);
}
}
class ProfileScreen extends React.Component {
constructor(props) {
this.state = {
userProfile: null,
}
}
componentDidMount() {
AsyncStorage.getItem('userProfile', (errs, result) => {
this.setState({userProfile: JSON.parse(result)});
});
}
save() {
var userSavedProfile = this.state.userProfile;
userSavedProfile.firstname = "Peter";
userSavedProfile.avatar = "peter-avatar.png";
userSavedProfile.location = "EEUU";
this.setState({userProfile: userSavedProfile});
AsyncStorage.setItem('userProfile', JSON.stringify(this.state.userProfile), () => {});
//this is the magic
this.props.navigation.state.params.getUserData();
}
render() {
return (
<View>
<Button title="Save" onPress={() => this.save()} />
</View>
);
}
}
2 On HomeScreen Constructor add this (Dirty one)
this.props.navigation.addListener(
'didFocus',
payload => {
this.setState({is_updated:true});
}
);
You can use componentDidUpdate(){...} insted componentDidMount(){}