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
Related
In times past, my colleagues and I would typically write React Testing Library (RTL) tests for the main parent components, which often have many nested child components. That testing made sense and worked well. Btw the child components in question are very much dedicated to that parent component and not of the reusable variety.
But now we're trying to write RTL tests for every single component. Today I was trying to build tests for an Alerts component, which is the parent of an Alert component and about 4 levels down from the top-level component. Here's some sample code in my test file:
function renderDom(component, store) {
return {
...render(<Provider store={store}>{component}</Provider>),
store,
};
}
let store = configureStore(_initialState);
const spy = jest.spyOn(store, 'dispatch');
const { queryByTestId, queryByText, debug } = renderDom(
<Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} />,
store
);
I then started writing the typical RTL code to get the Alerts component to do its thing. One of these was to click on a button which would trigger an ADD_ALERT action. I stepped through all of the code and the Redux reducer was apparently working correctly with a new alert, as I intended, yet back in the Alerts component, question.alerts remained null whereas in the production code it was definitely being updated properly with a new alert.
I spoke with a colleague and he said that for this type of test, I would need to artificially rerender the component like this:
rerender(<Provider store={store}><Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} /></Provider>);
I tried this and it appears to be a solution. I don't fully understand why I have to do this and thought I'd reach out to the community to see if there was a way I could avoid using rerender.
It's hard to be certain without seeing more of your code, but my typical approach with RTL is to take the fireEvent call that simulates clicking the button and wrap it in an act call. This should cause React to finish processing any events from your event, update states, rerender, etc.
Alternatively, if you know that a particular DOM change should occur as a result of firing the event, you can use waitFor. An example from the React Testing Library intro:
render(<Fetch url="/greeting" />)
fireEvent.click(screen.getByText('Load Greeting'))
await waitFor(() => screen.getByRole('alert'))
Will conditional rendering out of an object code split and lazy load as expected? Here's a short example of what I'm talking about.
const Component1 = lazy(() => import('some path'));
const Component2 = lazy(() => import('some path'));
const Component3 = lazy(() => import('some path'));
render () {
const { selectionIndex } = this.state;
<Suspense fallback={<div>Loading...</div>}>
{{
one: <Component1 />,
two: <Component2 />,
three: <Component3 />,
}[selectionIndex]}
</Suspense>
}
I want to know whether all three components will load on render, or just the one selected by selectionIndex. I'm trying to use this to conditionally select something to display based on a menu set by state, but I don't want to load everything at once.
They will not get rendered all at once. You can experiment by yourself, put console.log inside components is an easy way to find out.
React for web consists of two libs, "react" and "react-dom". "react" is in charge of encapsulating your logic intention into declarative data structures, while "react-dom" consumes these data structures and handles the actual "rendering" part of job.
The JSX element creation syntax <Component {…props} /> translates to plain JS as an API call to React.createElement(Component, props). The return value of this API call is actually just a plain object of certain shape that roughly looks like:
{
type: Component,
props: props
}
This is the aforementioned "declarative data structure". You can inspect it in console.
As you can see, calling React.createElement just return such data structure, it will not directly call the .render() method or functional component’s function body. The data structure is submitted to "react-dom" lib to be eventually "rendered".
So your example code just create those data structures, but the related component will not be rendered.
seems like its conditionally loaded based on selectionIndex. all the other components are not loaded at once.
P.S.: if you ever feel like which will get load first, just put a console log in that component and debug easily
conditionally load demo link - if you open this, the components are being loaded initially based on selectionIndex value being "one".
I'm not going to go into too much technical detail, because I feel like #hackape already provided you with a great answer as to why, point of my answer is just to explain how (to check it)
In general, I'd recommend you to download download the React Developer Tools
chrome link
firefox link
and then you can check which components are being rendered if you open the components tab inside your developer console. Here's a sandboxed example, best way to find out is to test it yourself afterall :-)
As you can see in the developer tools (bottom right), only the currently set element is being rendered
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.
According to the react documentation: http://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount we are adviced use componentDidMount for AJAX call that should be issued when a component is brought into view.
However, when switching between to instances of the same component with different props, componentDidMount is only called for the first component. So what are we supposed to do in this situation?
Currently I have the following workaround: In componentDidMount i do my AJAX call and In componentDidUpdate I compare old and new props to check if I am on a new "instance", and if so I do my AJAX call. But that seems exactly like a workaround. So my question is: is this really the way to do it?
I am aware that I could wrap my component in different empty components to solve my problem. However, this is not possible because we are building a data driven application that uses configurable components and it makes sense to use the same component with different configurations - which is where I'm running into problems.
I am aware that we are actually talking about react elements and not instances as such - witch I guess is part of the problem. Probably I have different react elements utilizing the same instance.
I have made a tiny example to illustrate the react behavior, using plain react (just to make sure I wasn't tricked by react-router or redux and what else we are using the real app):
class Foo extends React.Component {
componentDidMount() {
console.log('componentDidMount ' + this.props.foo);
}
componentDidUpdate() {
console.log('componentDidUpdate ' + this.props.foo);
}
render() {
return <div>Route is {this.props.foo}</div>;
}
}
function navigated() {
ReactDOM.render(
<Foo foo={window.location.hash} />,
document.getElementById('app')
);
}
window.addEventListener('hashchange', navigated, false);
navigated();
Initially when I go to #/bar I get 'componentDidMount #/bar' and when I go to #/baz i get 'componentDidUpdate #/baz'.
I seems like this unanswered question is a specific case of the same issue: React does not know when i render the same component
You can add the key property with unique value for each of hashes:
ReactDOM.render(
<Component hash={hash} key={hash} />, domNode
);
This will update the component every time when the hash is really changed.
https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
TL DR - your 'workaround' looks correct to me
When you initially render the component componentDidMount is called, when you change the hash prop componentDidUpdate is called. It is still the same component, it is just that a specific prop has changed value. In your case, you have logic (running an AJAX call when hash changes) that is specific to your application. React does not known that the hash prop is special, you make is special by adding the logic in componentDidMount. So I believe you have a good interpretation of the React docs and this way of achieving your goal is perfectly valid.
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>