I'm having trouble with my Realtime Database integration inside my React Native project. I'v followed the install guide from here https://rnfirebase.io/database/usage(did the initial getting started install also). I'v tried reading a value from the database but NOTHING gets logged in the console.log.
import database from '#react-native-firebase/database';
function TestComponent( props ) {
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
database().ref('/did').once('value').then(snapshot => {
console.log('DATA: ', snapshot.val());
});
};
return (
<View>
<Button title="DATA" onPress={fetchData} />
</View>
);
}
My database structure looks like this:
I'v tried everything. Made the function async and not async, moved the function inside useEffect(), took out useEffect() and left a normal function that run with a button click. I'v tried to fetch the data using Axios and adding .json to the URL but NOTHING logs data, I don't know if the database doesn't answer or if the code is the problem. My database().ref() is working fine because when I log it points to the reference URL. Also my rules are true for both read and write so no problem there. Used Postman to make a fetch and it worked perfectly.
Related
I am trying to write a test that checks if the screen is showing a Toast with an error message. The test passes, but there is a warning:
console.error
Warning: You called act(async () => ...) without await.
This could lead to unexpected testing behaviour, interleaving multiple act calls
and mixing their scopes. You should - await act(async () => ...);
The screen is working fine, I am just learning how to write tests. This is my test:
it('shows error correctly', async () => {
mockAxios.get.mockRejectedValueOnce(new Error('Async error'))
const { queryByText } = renderWithRedux(<DiscoverScreen />)
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
await waitForElementToBeRemoved(() => queryByText(ErrorMessages.GeneralErrorToast), { timeout: 5000 })
})
What am I not doing right? Definitely there is an issue with react native testing, because there are problems for certain async querying, especially when you have several of them. I found that here: https://github.com/callstack/react-native-testing-library/issues/379#issuecomment-720734366
I am using native base for showing the Toast, which is using Animated I think. Should I use jest.useFakeTimers() and how?
After researching how the Toast in native base works (this could be done when you open the source code in github - https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/ToastContainer.js), I found that it uses Animated.timing.
So I had to find out how to deal with react native animations in tests. That article had a solution that worked for me: https://medium.com/#joncardasis/react-native-how-to-test-components-implementing-animated-with-jest-8cabb5fc2730
After I added the code in my jest setup file, this is how my test looks:
global.withAnimatedTimeTravelEnabled(async () => {
const { queryByText } = renderedComponent
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
global.timeTravel(Constants.ErrorToastDelay)
expect(queryByText(ErrorMessages.GeneralErrorToast)).toBeNull()
})
It works and now the test passes with no warnings!
One little adjustment in my jest configuration was also missing. I had to add this:
"testEnvironment": "jsdom"
I hope this could help someone else, too!
I have a logout Button that binds to the following method.
async onLogoutButtonPress() {
await AsyncStorage.removeItem('accessToken');
await AsyncStorage.removeItem('mobileNumber');
await AsyncStorage.removeItem("businessId");
await AsyncStorage.clear();
this.props.navigation.navigate('Login');
}
I understand that AsyncStorage.removeItem is an asynchronous operation and it returns a Promise. However notice that I am using await that waits for Promise to resolve before navigating to Login.
I also tried the alternate then syntax
AsyncStorage.removeItem(('accessToken')).then(() => {console.log('resolved') },
() => { console.log('rejected') })
And all of them show as resolved in the console, but some how the items persist unless I close and restart the app. I am seeing this behavior on Android.
Why aren't the items getting removed until I restart the app ?
I am using expo for the build, if that matters.
I am using
import { AsyncStorage} from 'react-native';
Here's the react-native version.
"react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
In my react native (Expo) application the user has the possibility to select images for a group from the gallery to upload them to the server.
When the User selects the images in the Mediapicker Screen i store the list of the uris in the Asyncstorage and navigate back to the "group screen".
Here i read the data from the asyncstorage
_retrieveUpload = async () => {
try {
const value = await AsyncStorage.getItem("upload_que");
if (value !== null) {
this.setState({ uploadQueue: JSON.parse(value) });
}
} catch (error) {
console.log(error);
}
};
in the next step i created a component which takes this.state.uploadQueue as property, iterate over the values and upload them to the server.
This is working as long as the component is mounted and the user does not leave the screen.
I read that there is no possibility for a background-task in expo, but how can i start something like a service which runs independent from the actual mounted component?
First of all, can you post you Upload Component source code for more details.
Also please try in your upload code if you have await syntax, remove it and work with 'promise' and 'then' syntax if you need to do something after upload.
I'm writing a simple app with React Native and Expo.
This app has ~10 small to medium sized images that are used in different places within the app.
From what I read, unless I cache these images, they will be required to be downloaded from expo each time.
For this reason, I have noticed that they seem to load in really slowly when testing the app. Upon building and navigating through the app, I find that it takes a few seconds for my images to pop up even after the rest of the page has loaded.
I followed the setup as seen in the starting template.
Here is what my App.js looks like (I am using react-navigation so it varies from the sample file above):
export default class App extends React.Component {
state = {
isLoadingComplete: false
};
componentDidMount() {
StatusBar.setHidden(true);
}
render() {
_loadResourcesAsync = async () => {
return Promise.all([
Asset.loadAsync([
require("./assets/syria.png"),
require("./assets/lebanon.png"),
require("./assets/kenya.png"),
require("./assets/indonesia.png"),
require("./assets/somalia.png"),
require("./assets/india.png"),
require("./assets/america.png"),
require("./assets/albania.png"),
require("./assets/bosnia.png")
])
]);
};
_handleLoadingError = error => {
Alert.alert(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
if (this.state.isLoadingComplete == false) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<AppContainer
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
);
}
}
}
I have excluded my react-navigation code for the sake of brevity.
When I run this, my app gets stuck at Downloading JavaScript bundle 100.00%.
It seems that the _handleFinishLoading never runs. At least, that's the only reason I can see for it to never finish loading.
Given the small amount of images, I don't know how this could take more than a second. Instead it sits at the splash screen forever.
Any ideas on what I might be doing wrong here?
Found the solution:
I made a simple error. The async functions (_loadResourcesAsync, _handleFinishLoading, etc) need to be outside the render method. Moving them below my render method inside of the app class caused this to work as expected.
I am looking for a way to optimize the startup time of a pure react native mobile app.
As a JavaScript framework, is that possible to bundle the JavaScript files into separated files, say something like common.js and app.js. I was searching via Google with keywords something like "react native webpack" stuff but it seems like all these libraries are deprecated or out of date, such as react-native-webpack-server, react-native-webpack-starter-kit etc.
I am wondering if anybody here is also looking for a way to optimize the JavaScript bundle in react native. Or, maybe these third party bundle approach has been overcame by Facebook standard bundle?
You could dynamically load your component, in this way your bundle.js will contain only the fraction of js needed and as you navigate you will request the other different parts / fractions.
Rather than do the traditional way: import App from './containers/App/App'; you could do something like this:
class ImportedComponent extends Component {
state = {
component: null
}
componentWillMount() {
this.props.load()
.then((mod) => this.setState(() => ({
component: mod.default
})))
}
render() {
return this.props.children(this.state.component)
}
}
const App = (props) => (
<ImportedComponent load={() => import('./containers/App/App')}>
{(Component) => Component === null ? <h6 className="loading-message">Loading...</h6> : <Component {...props}/>}
</ImportedComponent>
)
or you can lazy load your component itself. Let's say for example that I have Moment JS and I don't want to load it until it's needed. so what I could do:
1) Create a state and set it to null.
constructor(props){
super(props);
this.state = {
lazyLoadedComponent: () => null
}
}
2) Use async componentDidMount with await, try and catch and update the state lazyLoadedComponent on componentDidMount
async componentDidMount(){
try {
const Moment = await import('react-moment');
this.setState({ lazyLoadedComponent: (data)=>{
return React.createElement(Moment.default, {format:'MM/DD/YY'}, data)
}
});
}
catch(err) {
this.setState({ lazyLoadedComponent: <div>{`Failed to load component: ${err}`}</div> });
}
}
3) Call the component on the render:
{this.state.lazyLoadedComponent(value.createdOn)}
By following these 2 examples you should, hopefully, be looking at a bundle.js under 250KB.
As a possible solution you can use ram-bundle format, that metro bundler provides.
In this case you will not load the entire js-bundle - you will load only part, that you need at a startup (in a lot of application are a lot of places, which user may not even see, and this feature allow you load such parts, only when they are required). So you can simplify your entry point and load only small piece of your bundle.
You can look at react-native-bundle-splitter. This library well integrated with almost all popular navigation libraries and allows you to postpone a loading of specific routes. For example, if you have a login screen, you can load at start up only this screen, and all others load in background or start the loading of them, only when user can see them.