How to know component is ready to render? - react-native

I am new in React-Native and I am stuck at one place to do in React-Native.
This is my one component :-
const ConentQR = (
content: string,
logo: Image.propTypes.source,
logoSize: number,
) => {
return (
<QRCode
color={Colors.DARK_PURPLE}
content={content}
codeStyle={'dot'}
outerEyeStyle={'diamond'}
logo={logo}
logoSize={logoSize}
backgroundColor={'transparent'}
/>
);
};
The problem is I am facing in react native,This component (<QRCode) take some time to generate QR, so how I can put this block in any background thread so that I can show loader to user until it get prepared. I am getting stuck at this place and unable to resolve that for while.
Please help anyone

You could use the InteractionManager to schedule tasks in the background
import { InteractionManager } from 'react-native'
const ConentQR = (
content: string,
logo: Image.propTypes.source,
logoSize: number,
) => {
// Use a state variable to track the QR code
const [qrCode, setQrCode] = useState(null)
// Use the InteractionManager to run the generation process in the background
InteractionManager.runAfterInteractions(() => {
// Generate the QR code in the background
const qrCode = generateQRCode(content, logo, logoSize)
setQrCode(qrCode)
})
// Show a loading spinner while the QR code is generated
// so the user knows something is happening in the background
if (!qrCode) {
return <ActivityIndicator />
}
// Render the QR code when it's ready
return <QRCode {...qrCode} />
}
I am using useState to keep track on the qr code and InteractionManager.runAfterInteractions to schedule the generation in the background. If the qr code is not ready it displays a loader. The generation happens in the generateQRCode() function.

Related

ReactNative UI freezing for a second before rendering a component with a fetch in useEffect()

TL;DR: My UI freezes for .5-1s when I try to render a component that does a API fetch within a useEffect().
I have ComponentX which is a component that fetches data from an API in a useEffect() via a redux dispatch. I'm using RTK to build my redux store.
function ComponentX() {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(fetchListData()); // fetch list data is a redux thunk.
}, [dispatch]);
...
return <FlatList data={data} /> // pseudo code
}
as you can see the fetch will happen everytime the component is rendered.
Now I have ComponentX in App along with another component called ComponentY.
Here's a rudamentary implementation on how my app determines which component to show. Pretend each component has a button that executes the onClick
function App() {
const [componentToRender, setComponentToRender] = useState("x");
if (componentToRender === "x") {
return <ComponentX onClick={() => setComponentToRender("y")}/>
} else {
return <ComponentY onClick={() => setComponentToRender("x")}/>
}
}
Now the issue happens when I try to move from ComponentY to ComponentX. When I click the "back" button on ComponentY the UI will freeze for .5-1s then show ComponentX. Removing the dispatch(fetchListData()); from the useEffect fixes the issue but obviously I can't do that since I need the data from the API.
Another fascinating thing is that I tried wrapping the dispatch in an if statement assuming that it would prevent a data fetch thus resolving the "lag" when shouldReload is false. The UI still froze before rendering ComponentX.
useEffect(() => {
if (shouldReload) { // assume this is false
console.log("reloading");
dispatch(fetchListData());
}
}, [dispatch, shouldReload]);
Any idea what's going on here?
EDIT:
I've done a little more pruning of code trying to simplify things. What I found that removing redux from the equation fixes the issue. By simply doing below, the lag disappears. This leads me to believe it has something to do with Redux/RTK.
const [listData, setListData] = useState([]);
useEffect(() => {
getListData().then(setListData)
}, []);
Sometimes running the code after interactions/animations completed solves the issue.
useEffect(() => {
InteractionManager.runAfterInteractions(() => {
dispatch(fetchListData());
});
}, [dispatch]);

React Native useState change from a function is not re-rendering the components

I've tried a lot of answers given to similar questions but nothing is fixing my problem as I'm changing the state in a function which I'm calling on the press of a button in a third-part library I'm using.
I'm using react-native-horizontal-date-picker and upon select a date I'm calling a function in which I'm changing the state.
<HorizontalDatePicker
pickerType={'date'}
dayFormat={'Do'}
monthFormat={'MMM'}
yearFormat={'YYYY'}
returnDateFormat={'DD-MM-YYYY'}
onDateSelected={(date) => {
if (date) {
getTimeData()
}
}}
/>
Then in my getTimeData function I'm updating the const [timeSlots, setTimeSlots] = useState([]); state:
function getTimeData() {
...
if (timeAvailable) {
setTimeSlots(finalTimeSlots)
} else if (!timeAvailable) {
setTimeSlots([])
}
...
}
based on the timeSlots array I'm updating the UI, now I have to tap twice on the date to be able to see the results getting rendered.
I'm passing timeSlots to another custom component that I made:
<TimePicker timeSlotsArray={timeSlots} />
How do I achieve this in this scenario?

Query in react native about sliding up panel

In React Native iOS, I would like to slide in and out of a like in the following picture.
So I installed this https://github.com/octopitus/rn-sliding-up-panel for ease.
but this error is showing =>
i cant understand whats wrong, I am new to react native. Please Help!
You cannot access variable called _panel from this object because you are inside a function itself. besides you are using function based react, in order to create a reference check useRef() hook or switch to class based component and then you can use this._panel;
smthg like this:
function AccessingElement() {
const elementRef = useRef();
const onPress = () => {
// e.g
elementRef.current.show();
}
return (
<View ref={elementRef}>
...child views
</View>
);
}

Fetching Data From Server Using iOS Device in React Native

I have just started learning React Native and development for mobile devices. One of the things I've tried is using fetch API to get data from http://jsonplaceholder.typicode.com/posts
The App.js file is given below:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
export default function App() {
const [firstLoad, setLoad] = React.useState(true);
const [data, upDateData] = React.useState([]);
let isLoading = true;
async function sampleFunc() {
let response = await fetch("https://jsonplaceholder.typicode.com/posts");
let body = await response.json();
upDateData(body);
}
if (firstLoad) {
sampleFunc();
setLoad(false);
}
if (data.length > 0) isLoading = false;
const posts = data.map(post => (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
));
return (
<View style={styles.container}>
{isLoading ?
<Text>Loading</Text> :
<Text>{posts}</Text>
}
</View>
);
}
Nothing fancy is going on here, just making an https request to the server to get posts. While the data is being transferred, the Loading label is being displayed, after that, all fetched posts are rendered on the page.
I am using Expo, and everything works fine when I run it in the browser, but when I scan the QR code, Expo app opens, the Loading message is displayed for a couple of seconds, and then the app crashes.
I may be doing something here that is typical of regular React and is not used in React Native. It is just strange that it would work on my computer and not the phone. Any suggestions would be greatly appreciated. Thank you in advance!
You cannot have text outside of Text components in react-native.
In your example, in the post function, you use the h1 and p tags, which are not supported by react-native.
The fix here is to make sure that those texts are inside Text components, you can have styling set to those to make them look closer to what you want.
You can refer the docs on how to create custom styles.
const posts = data.map(post => (
<View>
<Text>{post.title}</Text>
<Text>{post.body}</Text>
</View>
));
To debug similar issues in the future, you should be getting a red flashing screen with the exception. (Maybe it doesn't appear when running on Expo)

React Native: How can I use the DeviceInfo isTablet() method to conditionally apply a style to an element?

I am trying to adapt the design of my app to tablet and one way to detect if the app is running on a tablet is by using the DeviceInfo module in particular the isTablet() method. How can I use this method to conditionally apply styles to an element?
Here is what I am trying to do at the moment:
import { checkIfDeviceIsTablet } from './helper-functions';
<View style={[styles.wrapper, checkIfDeviceIsTablet() === true ? styles.wrapperTablet : {}]}>
{contents}
</View>
The checkIfDeviceIsTablet() function is as follows:
import DeviceInfo from 'react-native-device-info';
function checkIfDeviceIsTablet() {
DeviceInfo.isTablet().then(isTablet => {
return isTablet;
});
}
The issue is that when the component loads the checkIfDeviceIsTablet() method returns a promise as opposed to the expected true/false value and so the conditional styles are not applied when the app is run on a tablet. I tried turning the function into an async/await format with a try/catch but the result is the same.
I would use React Native's own Platform.isPad function but the app must also work on Android.
Any help is appreciated.
I would recommend calling DeviceInfo.isTablet() only once at the beginning of your app. You can store the result globally, and then later on you can check the type without having to deal with async promises.
To store the type globally, your options are:
A global variable
React's Context API
A static property on a class (if using ES6+)
Some sort of global state management solution like Redux
You still have to deal with the initial async problem, since the first call to DeviceInfo.isTablet() will return an async promise.
I'd recommend looking into React's Context API.
Here's a rough example:
render() {
return (
<DeviceInfoContext.Consumer>
{ ({ isTablet }) => (
<Text>Is this a tablet? {isTablet}</Text>
) }
</DeviceInfoContext.Consumer>
)
}
And your DeviceInfoContext class would look something like this:
class DeviceInfoContext extends React.Component {
state = {
isTablet: false
}
componentDidMount() {
Device.IsTablet().then(result => this.setState({ isTablet: result }))
}
render() {
return (
this.props.children({ isTablet: this.state.isTablet })
)
}
}
This is just a rough example. You can learn more about the Context API in the docs
Me too had some troubles with the breaking changes of react native 0.5xx to 0.6xx. The library for device detection change it structure to promises. A paintful.
This library save the day, the installation and use is very easy.
https://github.com/m0ngr31/react-native-device-detection
import { isTablet } from 'react-native-device-detection;
// isTablet is a boolean. Return false o true immediately
//So ...
import styled from 'styled-components/native';
import theme from 'styled-theming';
import { isTablet } from 'react-native-device-detection';
const CoverPageDateText = styled.Text`
font-size: ${isTablet ? 23 : 17};
color: gray;
padding-bottom: 9;
`