Lazy Load specific tab with React-Navigation v4 - reactjs

I am using React-Navigation V4 and the question is, Is there any way of lazy load only specific tab like if i have four tabs and i want to load two tabs after initialisation of tabs component and don't want to load other two these two only will load when user activates them.
if i use lazy: true in React it'll work work all tabs or either lazy load will be disabled for all or enable for all.

Unfortunately there is not such a thing in react navigation v4. but if you want to achieve performance you can use other methods to kind of lazy load part of screen.
const TabPage = (props) => {
const [renderHeavy, setRender] = useState(false)
useEffect(() => {
InteractionManager.runAfterInteraction(() => setRender(true))
}, [])
return (
<View style={styles.body}>
{
renderHeavy &&
<HeavyComponent />
}
<AnotherComponent />
</View>
)
}

Related

React good practise to render one screen inside another when one screen component needs almost everything from another screen component?

Im using react native and I have (among others) 2 screens. SearchScreen Component and ReportScreen Component. In my ReportScreen I basically need everything from SearchScreen (I render the search input field and the autocomplete component for the search suggestions plus all the functions from my component) except the SearchResults, as in my report component I will 1st search , and 2nd report something make a post request. I could have a conditional for rendering the searchResults and then render searchscreen inside reportScreen, but that seems bad practise. I cant see how I can refactor it and would love to get some advise what to do in such situation based on good practise. Thank you!!
const SearchScreen = ({navigation}) => {
// setting state...
const fetchData = async () => {...};
useEffect(() => {
fetchData();
}, []);
const updateSearch = item => {...}
const submitSearch = async (id) => {...}
return (
<View style={styles.container}>
<Search //component with search input bar
display={display}
setDisplay={setDisplay}
searchInput={searchInput}
onSearchInputChange={(input) => setSearchInput(input)}
showCancelIcon={showCancelIcon}
handleShowCancelIcon={(input) => setShowCancelIcon(input)}
/>
<AutoComplete
display={display}
setDisplay={setDisplay}
searchInput={searchInput}
submitSearch={submitSearch}
onSearchInputChange={updateSearch}
data={data}
/>
<SearchResult
searchResult={searchResult}
submitNewSearch={submitSearch}
/>
</View>
)
}
}
You can develop a HOC(Higher Order Component) that will implement the common items of your components.
Refer to this link to know more about HOC: https://reactjs.org/docs/higher-order-components.html

React Native Navigation, open universal link from within own app

I am using React Navigation for React Native. I have successfully configured it to handle universal link, which is something like this
// linking.ts
import { APP_ID } from '#env';
const config = {
screens: {
LoginScreen: 'authorize',
RegisterScreen: 'register',
CustomerStack: {
screens: {
OrderDetailScreen: 'customer/order/:orderId',
},
},
},
};
const linking = {
prefixes: [`${APP_ID}://app/`, 'https://example.com/app/'],
config,
};
export default linking;
// App.tsx
import linking from './linking'
const App = () => {
return (
<NavigationContainer linking={linking}> <MyApp /> </NavigationContainer>
)
}
When I press a link in the browser such as https://example.com/app/customer/order/1234, then it successfully opens my app's order page.
Problem
I want to be able to open the url such as https://example.com/app/customer/order/1234 indside my app and have it open the order page. I have tried
<Button onPress={() => Linking.openURL('https://example.com/app/customer/order/1234')} />
but (testing on IOS) it switch to the web browser first to open the link, and then open my app.
Is it possible to open the order page directly inside my app without switching to the browser first.
Note: I am trying to implement an in-app notification history page, each notification item has the link saved in the database, and when the user clicks on the item I want to navigate the user to the page as configured in linking.ts. I know it is possible to parse the link and use navigation.navigate() instead, but that means I will have 2 places for the linking configuration. I think it would be great if I can reuse the existing logic provided by React Navigation.
In React Navigation, you can use the useLinkTo hook. This hook allows you to navigate inside the application using a path.
This will allow you to use the following option:
const linkTo = useLinkTo();
return (
<Button onPress={() => linkTo('/customer/order/1234')} />
);
If using a URL is mandatory, then you can use extractPathFromURL, an internal React Navigation function, to remove the prefix.
import extractPathFromURL from '#react-navigation/native/src/extractPathFromURL';
import linking from './linking'
// ...
const linkTo = useLinkTo();
return (
<Button onPress={() => {
const path = extractPathFromURL(linking.prefixes, 'https://example.com/app/customer/order/1234');
const pathWithSlash = path.startsWith('/') ? path : '/' + path;
linkTo(pathWithSlash);
} />
);
extractPathFromURL is not part of the official API and may be removed in future versions. For reliability, you can create a duplicate of this function in the project.

Window.location.reload() in react-native

I often use window.location.reload on my React Web project.
window.location.reload();
Is there any similar way to reload page(component) in react-native?
The thing that you mention is a browser feature. React Native uses native capabilities to render your app, so there are no browser features like window.location.reload()
So I am not sure what is your particular use-case for this. I guess it is needed to reload the current screen, not the full app.
In this case, you should use a react-way to re-render your screens. In React the views are re-rendered when the props or state changes. So if you want to trigger a full reload, you need to have some internal state that will trigger a reload. For example, you can use key property and a container with an internal state that toggles this key.
But I would consider it a hack. You should really use the react data-driven views
You can read more about key prop either in official docs or check out this article from Google: https://kentcdodds.com/blog/understanding-reacts-key-prop/
import React from 'react';
import { View, Text } from 'react-native';
const Container = () => {
const [key, setKey] = React.useState(0);
const reload = React.useCallback(() => setKey((prevKey) => prevKey + 1), []);
return <Child reload={reload} key={key} />;
}
const Child = ({ reload }) => {
const getRandomId = () => parseInt(Math.random() * 100, 10);
// We use useRef to showcase that the view is fully re-rendered. Use ref is initialized once per lifecycle of the React component
const id = React.useRef(getRandomId());
return (
<View>
<Text>My random id is {id}</Text>
<Button onPress={reload} />
</View>
)
}
#Coding is Life
import { NavigationEvents } from 'react-navigation';
<NavigationEvents onWillFocus={() => this.goBackReload()}/>
This is the way to reload the page, ie. when you go back to page you got the call back method. Using this method clear all state value and refresh the page. Another way is refresh control using to reload the app.
<ScrollView refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh} />
}>
</ScrollView>
When you scroll down the screen onRefresh method trigger.

React Router <Link> to dynamic page and go back to previous state in useEffect

I'm trying to make a React app with dynamic pages and navigational fetch with next and previous buttons, when click on the Item it shows the page dynamic page but when I press the back button on the browser it forgets the state where count and input value and shows the initial state. What should I write to save the state so that when I go back it stays on the same count and value and not start from initial state?
const App = () => {
return (
<Router>
Route path='/item/:id' component={Item} />
Route path='/' exact component={Search} />
</Router>
)
}
const Items = (props) => {
return (
<div>
{props.data.map(image => (
/* Link to component Item */
<Link to={`/item/${image.id}`} key={image.id}>
<img src={image.urls.small} alt={image.description} />
</Link>
))}
</div>
);
}
const Search = () => {
useEffect(() => {
getData();
},[count]);
const nextPage = (event) => {
setCount(count + 1);
getData();
event.preventDefault();
}
const prevPage = event => {
if (count > 1) {
setCount(count - 1);
getData();
}
event.preventDefault();
}
return (
<div>
<Items data={images} />
<button onClick={prevPage}>PREV</button>
<button onClick={nextPage}>NEXT</button>
</div>
);
}
It looks like you may need to look into state management systems/patterns. What you are looking for is called persistence, where you can revisit a page and maintain the same values in the same session.
Your form state is being cleared because you're holding it in a "local" component state - a component that is being re-rendered every time you switch pages. The "easy" way to solve this is to manage the Search component's state inside of App and add all the handlers to App as well, this is also called "raising" the state. While this is the "easy" way to do things, it is also quick and dirty (you will probably need to refactor in the future) because it will eventually overcomplicate your App component if you add other page/form states, etc.
The other way (also quick and easy and what I would recommend) is to store your search values in localStorage. You can save them in a JSON format to be read and used to update the form as soon as the component mounts. I think if this is a small app, this is probably the best way.
The approach for larger applications (3+ pages) is to use a global state management system, Flux, Redux, Mobx etc. These systems make it much easier to maintain a global app state and persist information even if you navigate through different pages in the app (as long as you maintain the session). This approach, while I recommend you look into for practice is usually left fort larger applications and can add too much overhead for what its worth on smaller apps.
Another 'quick win' approach is to utilize ReactRouter's state. Basically you can set state properties to a particular point in the router's history. I've used this before to remember scroll position of a lazy-loading grid.
What this does is applies your state to the current browser history position, so when you navigate elsewhere and then hit back on your browser navigation bar, the previous state is also restored (not just the URL).
In essence, create your own Link component wrapper:
import React from 'react';
import { withRouter } from 'react-router';
import { Link } from 'react-router-dom';
class CustomLink extends React.Component {
handleClick(e) {
const { count, input, history } = this.props;
state = { count, input }
history.replace({ state });
}
render() {
const { to, children } = this.props;
return (
<Link
onClick={this.handleClick}
to={to}
>
{children}
</Link>
);
}
}
export default withRouter(CustomLink);

Nested navigators inside a custom navigator? Resulting in multiple routers

I have a nested PageWithStackNavigator here, inside this code from the React Navigation example for a custom tab view:
const CustomTabView = ({ descriptors, navigation }) => {
const { routes, index } = navigation.state;
const descriptor = descriptors[routes[index].key];
const ActiveScreen = descriptor.getComponent();
return (
<SafeAreaView>
<CustomTabBar navigation={navigation} />
<ActiveScreen navigation={descriptor.navigation} />
</SafeAreaView>
);;
};
const CustomTabRouter = TabRouter(
{
PageWithStackNavigator,
PageTwo,
},
{
initialRouteName: 'PageWithStackNavigator',
}
);
const navigator = createNavigator(CustomTabView, CustomTabRouter, {})
const CustomTabs = createNavigationContainer(navigator);
But, this is resulting in multiple routers. CustomTabRouter seen here, and one router in PageWithStackNavigator. "this.props.navigation.goBack()" is doing different things depending on if I call it inside the tabs, or inside PageWithStackNavigator.
Per the docs, I should be doing something like "static router = AuthenticationNavigator.router;", but I don't see how to do this with a custom TabRouter.
The issue was not that there were multiple routers, but rather in the goBack command.
From a comment on my github issue:
I know this is confusing but you need to use goBack(null). the goBack() helper automatically provides the key that you're going back from and is then limited to the stack that it's inside of. goBack(null) says to go back from anywhere.

Resources