Window.location.reload() in react-native - reactjs

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.

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 useEffect strange behaviour with custom layout component

I'm trying to use scroll position for my animations in my web portfolio. Since this portfolio use nextJS I can't rely on the window object, plus I'm using navigation wide slider so I'm not actually scrolling in the window but in a layout component called Page.
import React, { useEffect } from 'react';
import './page.css';
const Page = ({ children }) => {
useEffect(() => {
const scrollX = document.getElementsByClassName('page')
const scrollElement = scrollX[0];
console.log(scrollX.length)
console.log(scrollX)
scrollElement.addEventListener("scroll", function () {
console.log(scrollX[0].scrollTop)
});
return () => {
scrollElement.removeEventListener("scroll", () => { console.log('listener removed') })
}
}, [])
return <div className="page">{children}</div>;
};
export default Page;
Here is a production build : https://next-portfolio-kwn0390ih.vercel.app/
At loading, there is only one Page component in DOM.
The behaviour is as follow :
first listener is added at first Page mount, when navigating, listener is also added along with a new Page component in DOM.
as long as you navigate between the two pages, no new listener/page is added
if navigating to a third page, listener is then removed when the old Page is dismounted and a new listener for the third page is added when third page is mounted (etc...)
Problem is : when you navigate from first to second, everything looks fine, but if you go back to the first page you'll notice the console is logging the scrollX value of the second listener instead of the first. Each time you go on the second page it seems to add another listener to the same scrollElement even though it's not the same Page component.
How can I do this ? I'm guessing the two component are trying to access the same scrollElement somewhat :/
Thanks for your time.
Cool site. We don't have complete info, but I suspect there's an issue with trying to use document.getElementsByClassName('page')[0]. When you go to page 2, the log for scrollX gives an HTMLCollection with 2 elements. So there's an issue with which one is being targeted. I would consider using a refs instead. Like this:
import React, { useEffect, useRef } from 'react';
import './page.css';
const Page = ({ children }) => {
const pageRef = useRef(null)
const scrollListener = () => {
console.log(pageRef.current.scrollTop)
}
useEffect(() => {
pageRef.addEventListener("scroll", scrollListener );
return () => {
pageRef.removeEventListener("scroll", scrollListener )
}
}, [])
return <div ref={pageRef}>{children}</div>;
};
export default Page;
This is a lot cleaner and I think will reduce confusion between components about what dom element is being referenced for each scroll listener. As far as the third page goes, your scrollX is still logging the same HTMLElement collection, with 2 elements. According to your pattern, there should be 3. (Though there should really only be 1!) So something is not rendering properly on page 3.
If we see more code, it might uncover the error as being something else. If refs dont solve it, can you post how Page is implemented in the larger scope of things?
also, remove "junior" from the "junior developer" title - you won't regret it

Lazy Load specific tab with React-Navigation v4

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>
)
}

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);

How to make react router replace component only after the matched route asynchronously loaded..?

So here's the situation - When a Link is clicked the nprogress bar will start and I want react-router to only replace the current component with the matched route once that's done loading asynchronously.. just like in instagram..
But I am only getting this -
Here's my HOC to load component asynchronously --
import React, { useEffect, useState } from "react";
import nprogress from "nprogress";
import "nprogress/nprogress.css";
export default importComponent => props => {
const [loadedComponent, setComponent] = useState(null);
// this works like componentwillMount
if (!nprogress.isStarted()) nprogress.start();
if (loadedComponent) nprogress.done();
useEffect(() => {
let mounted = true;
mounted &&
importComponent().then(
({ default: C }) => mounted && setComponent(<C {...props} />)
);
// componentUnMount
return () => (mounted = false);
}, []);
// return the loaded component
const Component = loadedComponent || <div style={{ flexGrow: 1 }}>..</div>;
return Component;
};
I didn't find a solution to this anywhere on the internet.. so I am asking this question here in stackoverflow. I am hoping someone here can solve this.
Edit: 15-Nov-2021
The old approach doesn't work with routes that uses route params or query such as useLocation, useRouteMatch, etc.
The one I am using now is to use React.lazy, React.Suspense and updating the fallback prop whenever a page is rendered. So when the next page is being loaded, the fallback will be shown which is basically the same component instance as current page. Moreover, with this new approach, you can render the next page whenever you like; after fetching data from backend, or after animation, etc.
Here is a demo and source code.

Resources