dynamically update device info react native - javascript

I am using react-native-device-info to get DeviceLocale or DeviceCountry. But is there a way that I can update Device-info with out restarting the app.
For Example, on device settings If my language is set to "English(US)", Device-info cam detect that but if I change the settings to "English(CA)",Device-Info is not able to capture that until I restart the app.
Any help is really appreciated.

How are you changing the settings of the language?
If it's built in...you can do something like setting the language as a state of the component and once call the function to change the language settings, you can setState and it'll update the component with the new value.
If you're talking about hopping out of the app and manually changing the language settings then maybe setting creating a setInterval to check the device-info will work.

Assuming you're defining the reference to DeviceInfo outside of the component's class definition, you could try moving it inside like this.
class MyComponent extends React.Component {
checkDeviceInfo() {
let DeviceInfo = require('react-native-device-info');
let locale = DeviceInfo.getDeviceLocale();
// do something with locale
}
render() {
return (
<TouchableOpacity onPress={this.checkDeviceInfo.bind(this)}>
<Text>Touch Here</Text>
</TouchableOpacity>
)
}
}
You could put this in your component so that it's called whenever you want to check for DeviceInfo changes.

Related

Class works fine normally but stops working when instantiated

I'm making a bluetooth remote in React Native. I have a class BLE that works on it's own, but parts of it don't work when I it in another class.
import BLE from './Core/BLE.js'
const myBLE = new BLE();
function DebugScreen(){
useEffect(() => {
myBLE.componentDidMount();
}, []);
return(
<ScrollView>
<Text>State: {myBLE.state.info}</Text>
<Text>Devices: {JSON.stringify(myBLE.state.ble_devices)}</Text>
</ScrollView>
)
}
export default DebugScreen;
The devices text box shows data just fine, but state does not. I verified that this was not simply the state not refreshing by putting a timer on the screen.
My question is, is there a fundamental difference between code running in an instantiated class and one that isn't?
I figured out what I was doing wrong. Instantiating classes the normal JS way is bad practice in React/React Native. To have top level code and information, I need to use context https://reactjs.org/docs/context.html

Reactjs render and state change dependancies

I'm learning ReactJS at the moment and struggling to understand how to render/update content based on changes elsewhere.
Example:
I have a timer app, which includes pause/restart functionality. It contains a Start/Pause button.
Timer.js
export class Timer {
constructor(parentApp) {
this.app = app;
this.playing = false;
}
start() {
this.playing = true;
}
pause() {
this.playing = false;
}
}
Button.js
export class IconButtonBar extends Component {
constructor(props) {
super(props);
this.state = {label: 'Start'};
}
render() {
return (
<div className="IconButtonBar">
<Button label={this.state.label} />
</div>
);
}
}
I want to update the label:
Depending whether the timer is started/stopped at initial render
When the timer is manually started/stopped by pressing the button
When the timer is manually started/stopped by another means
When the timer is programatically started/stopped (e.g. limit reached)
In jQuery I'd probably fire a custom event trgger to the body tag:
$('body').on('start_playing', function() {
$('#playpause_button).text('Pause');
}
But there are probably much more 'native' ways to do this in ReactJS.
I hope this makes sense, and you can help!
You need to change your vision of how react app is structurized. Just forget about jQuery. React is declarative, you need to say it what should be rendered (surely it can be done another way, but why to use React then?).
You can use stateful component if you have no third-party store library.
You can use conditional redering for showing different elements depending on different state.
Here is an example stateful component of what you wanted to achieve: https://codesandbox.io/s/14vokm9q14
Just remember, it is not a good practice to keep state in a view layer. Consider using Redux/Mobx:
Redux - it will require a lot of boilerplate: creation of reducers, action-creators, action-types, handling side-effects (with redux-thunk/redux-saga/redux-observable), etc. But it is stable, reliable and easy-testable.
Mobx - something like you have in your example. It is MVVM, where you have model in its classical meaning, decorate properties as observable and just inject this model into your react component. After that you can just mutate properties of your model and these changes will reflect onto your view.

KeyboardAvoidingView - Reset height when Keyboard is hidden

I'm using React Natives KeyboardAvoidingView to set the height of my View when the Keyboard is shown. But when I close the the keyboard in the app, the height of the View is not changed back to it's original value.
<KeyboardAvoidingView behavior="height" style={styles.step}>
<View style={styles.stepHeader}>
// my content
</View>
</KeyboardAvoidingView>
The View with the red outline did take up the whole space before I opened and closed the keyboard.
A more detailed explanation on Nisarg's answer.
Create a key for the KeyboardAvoidingView in the constructor
constructor(props) {
this.state = {
keyboardAvoidingViewKey: 'keyboardAvoidingViewKey',
}
}
add listener on the keyboard's will/did hide (and remove it in the willUnmount)
import { KeyboardAvoidingView, Keyboard, Platform } from 'react-native'
componentDidMount() {
// using keyboardWillHide is better but it does not work for android
this.keyboardHideListener = Keyboard.addListener(Platform.OS === 'android' ? 'keyboardDidHide': 'keyboardWillHide', this.keyboardHideListener.bind(this));
}
componentWillUnmount() {
this.keyboardHideListener.remove()
}
update the keyboardAvoidingViewKey in the keyboardHideListener function, should be a new value each time (I used a timestamp) and use this key when rendering the KeyboardAvoidingView element.
keyboardHideListener() {
this.setState({
keyboardAvoidingViewKey:'keyboardAvoidingViewKey' + new Date().getTime()
});
}
render() {
let { keyboardAvoidingViewKey } = this.state
return (
<KeyboardAvoidingView behavior={'height'} key={keyboardAvoidingViewKey} style={...}>
...
</KeyboardAvoidingView>
)
}
Note:
Keep in mind that this will recreate the elements inside the KeyboardAvoidingView (i.e: will call their constructor function, I'm not quite sure why, I'll update the answer after deeper investigation), so you'll have to keep track of any state/prop values that might be overwritten
Update
After a much deeper investigation, I now know why the views are recreated once you change the key.
In order to truly understand why it happens, one must be familiar with how react-native dispatches the render commands to the native side, this particular explanation is pretty long, if it interests you, you can read my answer here. In short, react-native uses Reactjs to diff the changes that should be rendered, these diffs are then sent as commands to a component named UIManager, which sends imperative commands that translate into a layout tree, which changes the layout based on the diff commands.
Once you set a key on a component, reactjs uses this key to identify changes to said component, if this key changes, reactjs identifies the component as a completely new one, which in return sends the initial command to create said component, making all it's children to be created from scratch because there are identified as new elements in a new layout tree, deleting the old tree and creating a new one instead of just adjusting the diffs
If you would like, you can actually spy on these dispatched messages by adding the following code to your App.js file:
import MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue'
const spyFunction = (msg) => {
console.log(msg);
};
MessageQueue.spy(spyFunction);
If you do that, you'll notice in the logs that each time the key changes, the command that is dispatched in return is createViews, which like stated above creates all the elements that are nested under said component.
Please give key to KeyboardAvoidingView and change when keyboard open and close so it will render and take height
<KeyboardAvoidingView behavior="height" style={styles.step} key={values}>
Wrap components in <KeyboardAvoidingView behavior="padding" style={styles}> on iOS and <View style={styles}> on android
render() {
const ScrollContainer: View | KeyboardAvoidingView =
this.renderDependingOnPlatform();
const scrollContainerParams: any = {};
if (isIOS)
scrollContainerParams.behavior = "padding";
return (
<ScrollContainer style={styles.container} {...scrollContainerParams}>
Scroll and other components
</ScrollContainer>
)}
/**
* Render scroll container depending on platform
* #returns {any}
*/
renderDependingOnPlatform() {
if (isAndroid())
return View;
else return KeyboardAvoidingView;
}
A simple workaround is to set the behavior property of the KeyboardAvoidingView to 'padding'. This avoids the issue of recalling the constructor function, which allows you to safely store information in state (say you have two Inputs and you want to store the value of the text in state even if the user collapses the keyboard in between clicking the two inputs).
This method may slightly alter the layout of the KeyboardAvoidingView's children, so be aware of that.

Stop loading a new route/component if current component has changes

I have a requirement where I need to check if the local state has changes before the user navigates to the next tab. sort of like handing a component abandonment. I have come up with 2 options as follows,
Achieve this via componentWillUnmount. If its a good practice is there a way to conditionally stop the component being unmounted?
Via the window. As stated in the following solution : https://groups.google.com/forum/#!topic/reactjs/z63RGG1l_0U
Any idea on this matter is greatly appreciated :)
In the react-router-redux router is part of the state, so you can experiment on that.
To do so you should take a look at RouterContext, which provides setRouteLeaveHook function.
OR
As far as I remember there's also second option. Router object on context contains listenBeforeLeavingRoute
this.context.router.listenBeforeLeavingRoute
But its basically the same thing if you look at the source code of react-router. But its accessible from different layers.
EDIT:also Route has onLeave hook, may be useful!
Hope it helps somehow.
Regards,
Mariusz
How does the user navigate to the next tab? When you are using a <Link> you could define an unsavedChanges flag in your state. Set this to true (via dispatching an action and having a reducer responsible for that action) whenever you think that the user must not leave the current tab.
class Foo extends React.Component {
handleClick(e) {
const { unsavedChanges } = this.props
if(unsavedChanges) {
e.preventDefault()
}
}
render() {
return (
<Link to='/nextTab' onClick={this.handleClick}>Bar</Link>
)
}
}
Of course you need to pass unsavedChanges to your components props.

Advice about webapp architecture and reactJs

This question is more to know your opinions about the way I'm trying to solve this issue.
I would need a little bit of ReactJs expertise here as I'm quite new.
First a little bit of context. I'm developing a web application using ReactJs for the frontend part.
This webapp is going to have many translations, so for maintenance I thought it would be better to store all the translations in a database instead of having them into a file. This way I could manage them using sql scripts.
I'm using a MySQL database for the backend, but for performance reasons, I have added ElasticSearch as second database (well, it is more a full text search engine).
So once the application starts, the translations are automatically loaded into ElasticSearch. Every translation has a code, and a text, so in elastic search I only load the translations for one locale (by default english), and when a user switchs the locale, a call is done to load all the translations for the selected locale and update their corresponding text.
This way from the fronted I can reference a translation only by the code and I will get the text translated in the correct locale.
Now, how do I do that in react?
So far I have written a component TranslatedMessage which is basically looking for a given code and displaying it whereever this component is rendered.
Below the code of the component:
import React from 'react';
export class TranslatedMessage extends React.Component {
constructor() {
super();
this.render = this.render.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
this.state = {message: ''};
}
render() {
return (<div>{this.state.message}</div>);
}
componentDidMount() {
var component = this;
var code=this.props.code;
var url="data/translation?code="+code;
$.get(url, function (result) {
component.setState({message: result.text});
});
}
};
And then I use it in the application whis way, for example to translate the title of an 'a' link:
<TranslatedMessage code="lng.dropdown.home"/><i className="fa fa-chevron-down" />
So far is working fine but the problem is that I need to refresh the whole page to get the new translations displayed, because I'm not updating the state of the component.
So now my questions:
1)Every time that we find in a page the component TranslatedMessage, a new instance of that component is created right? so basically if I have 1000 translations, 1000 instances of that component will be created? And then React has to take care and watch all these instances for changes in the state? Would that be very bad for performance? Do you find any more efficient way to do it?
2) I don't think forcing the whole page to reload is the most proper way to do it, but how can I update the states of all that components when a user switch the locale? I've been reading about a framework (or pattern) called Flux, and maybe that could fit my needs, what do you thing, would you recommend it?
3) What do you think about storing translations on db, I'm a bit concern about sending a query to the db for every translation, would you recommend or not this approach?
Any suggestions, ideas for improvement are very welcome!
Thank you for taking your time to read it or for any help!
I use what is basically a Flux store for this purpose. On initialisation the application requests the whole language file to use (which is JSON) and that gets shoved into memory in the store, something like this (I'm going to assume a totally flat language file for now, and I'm using ES2015/16 syntax, I've omitted error checking etc etc for brevity):
class I18n {
constructor() {
this.lang = await fetch( 'lang_endpoint' )
.then( res => res.json() )
}
get( translation ) {
return this.lang[ translation ] || null
}
}
Somewhere my app starts during a ReactDOM.render( <App /> ) or some variation and this renders the whole thing top-down (I try to eliminate state as much as possible). If I needed to switch languages then I'd bind a change handler such that the store emits a change event which is heard by some code that triggers a ReactDOM.render. This is fairly standard Flux practise for changing the app state, the key is to try and eliminate state from your components and store it inside your stores.
To use the I18n class simply instantiate it somewhere (I normally have it as a singleton exported from a file, e.g. module.exports = new I18n(), require that file into your components and use the get method (this assumes some sort of packager such as browserify or webpack but it looks like you have that complexity all sorted):
import 'i18n' from 'stores/i18n'
class MyComponent extends React.Component {
constructor() { ... }
render() {
return (
<span>{ i18n.get( 'title' ) }</span>
)
}
}
This component could also be simplified to
const MyComponent = props => <span>{ i18n.get( 'title' ) }</span>

Categories

Resources