Prevent scroll to top after rerender in React Native - reactjs

I have problem with scroll to top after rerender component.
Is there any way to prevent this ? I dont want to scroll to top after any rerender just change the content.

This may not be the best way of doing it in React, but it should work.
As described in this page, there is a way to get the amount of pixels the user scrolled and store them in a variable. Then use that value to programmatically change the amount of scrolled pixels after the rerender. This is for Javascript, but it should also work for React.
Simple Example:
// Get the scroll pixels from the top of the page (Store this before rerender)
const scrollFromTop = document.documentElement.scrollTop;
// Update the scroll pixels from the top of the page with the stored int value (Do this after rerender)
document.documentElement.scrollTop = scrollFromTop;

you can give your component a state,
import React, {useState, useEffect} from 'react';
const MyComponent = props => {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
if (scrolled) {
return; // no more scrolling
}
setScrolled(true);
// TODO: scroll to top here
}, [scrolled]);
// ...
};
export default MyComponent;

Related

Resium: how to prevent map from redraw upon any change of state?

I've got a sample Resium project that shows the map. I've added a simple onClick that sets some state which is NOT being used anywhere - I just set it. Still, it causes the entire map to redraw & flicker UNLESS I remove the terrainProvider. Example (terrainProvider is commented out) if you move/click the mouse, the entire Cesium UI flickers and redraws everything. I'm using React 17, Resium 1.14.3 (Cesium 1.86.1). Any idea what's going on?
import React, { useState } from "react";
import { Cartesian3, Color } from "cesium";
import { Viewer, Entity } from "resium";
import * as Cesium from 'cesium';
export default function App() {
const [currentPos, setCurrentPos] = useState(null);
const handleMouseClick = (e, t) => {
setCurrentPos("xxx");
}
return (
<Viewer full
onClick={handleMouseClick}
onMouseMove={handleMouseClick}
// terrainProvider={new Cesium.CesiumTerrainProvider({ url: 'https://api.maptiler.com/tiles/terrain-quantized-mesh-v2/?key=xxxxxxx' })}
>
<Entity
name="Tokyo"
position={Cartesian3.fromDegrees(139.767052, 35.681167, 100)}
point={{ pixelSize: 10, color: Color.RED }}
/>
</Viewer>
);
}
My guess is this. No matters that the currentPos is not used in the component, is still part of the component's state, then the components re-renders.
In every render, the terrainProvider prop is receiving a new instance, so this causes that the entire Viewer re-renders too.
Maybe you can save your instance as state in the component and don't create a new one everytime that the component is re-render.

How do I prevent the page from scrolling when the usestate changes in react hook?

const [posttotal, settotal] = React.useState(0);
> here is changing useState value. and page scrolling to top.
function changeVerdigiHizmetler(slct,fiyat){
if (slct==true) {
settotal(posttotal+Number(fiyat))
} else {
settotal(posttotal-Number(fiyat))
}
}
function changeVerdigiHizmetler_EkHizmetler(event,adi,slct,fiyat){
changeVerdigiHizmetler(slct,fiyat)
}
**When I go to the onclick event, I get the price total and print it on the screen with posttotal.
There is no problem in operation.
But the page scrolls up because the useState has changed. I want to avoid this as it is annoying.
How can I prevent the page from scrolling?**

Restore scroll position after toggled a modal in react with useContext

Goal
Restore the scoll position of the window after the a div has toggled from position fixed to none.
Problem
Restoring the scroll position doesnt work although i get it saved correctly to the state.
Description
I have a page where a modal is opened via onClick. Therefore i created a "ToggleModalContext" to pass the props to the modal on the one hand and to the background div on the other hand. I want to modify the background div with setting the css property position to fixed to avoid that the background is scrolled instead of the content of the modal.
When the modal is closed, the position: fixed is removed and i want to restore the scroll position of the window.
This last step doesnt work. Maybe someone else has an idea?
ToggleModalContext (Thats the context, where the scroll restore function is called)
import React from "react";
export const ToggleModalContext = React.createContext();
export const ModalProvider = props => {
const [toggle, setToggle] = React.useState(false);
const [scrollPosition, setScrollPosition] = React.useState();
function handleToggle() {
if (toggle === false) {
setScrollPosition(window.pageYOffset); // When the Modal gets opened, the scrollposition is saved correctly
}
if (toggle === true) {
window.scrollTo(0, scrollPosition); // Restoring doesnt work.
}
setToggle(!toggle);
}
return (
<ToggleModalContext.Provider value={[toggle, handleToggle]}>
{props.children}
</ToggleModalContext.Provider>
);
};
Maybe somebody has a idea?
Maybe i have to use useEffect? But how?
Thanks for your time in advance :)
From the description you have provided, you are using a fixed position on the background div to remove scrolling on the window when you open your modal
On the other hand, you are calling
if (toggle === true) { window.scrollTo(0, scrollPosition);}
before your modal has closed. At this time, the background div is in a fixed position and there is no where to scroll to.
You need to ensure that your modal has safely closed and your background div is back to its normal position before calling this function. To see the behavior, you can use a setTimeout function and call this function there with a set time e.g.
setTimeout(() => window.scrollTo(0, scrollPosition), 2000);

How to know when React Bootstrap modal has been loaded?

I've built a modal with React Bootstrap that generally works fine. But when the browser is zoomed in, an inner div scrolls down a little, obscuring the top of the modal. So I added a useEffect to scroll back to the top when the modal is loaded. I was able to accomplish this in my functional React component like this:
// A reference to a `div` at the top of my modal
// Note: This approach won't work: const titleRef = useRef(null);
const titleRef = React.createRef();
useEffect(() => {
if (titleRef.current) {
const modalBodyContainer = document.getElementById('modalBodyContainer');
if (modalBodyContainer) {
modalBodyContainer.scrollTop = 0;
}
}
}, [titleRef]);
I need to add some more code so it's only run once, when first loaded but it otherwise does work correctly.
But I'm wondering if there's another way than using a ref in the way I have?

How do people handle scroll restoration with react-router v4?

I'm experiencing some problems with scroll positions on the back button (history popstate) when using react-router. React router v4 doesn't handle scroll management out of the box because browsers are implementing some automatic scroll behavior. This is great except when the height of the browser window changes too dramatically from one view to another. I have implemented the ScrollToTop component as described here: https://reacttraining.com/react-router/web/guides/scroll-restoration
This works great. When you click a link and go to a different component, the browser scrolls to the top (like a normal server-rendered website would). The issue only happens when you go back (via the browser back button) to a view with a much taller window height. It seems that (chrome) tries to go to the scroll position of the previous page before react has rendered the content (and browser height). This results in the scroll only going as far down as it can based on the height of the view it's coming from. Picture this scenario:
View1: Long list of movies (window height 3500px).
(movie is clicked)
View2: Details view of the selected movie (window height: 1000px).
(Browser back button is clicked)
Back to view 1, but scroll position can't go further than 1000px, because chrome is trying to set the position before react renders the long movie list.
For some reason this is only a problem in Chrome. Firefox and Safari seem to handle it fine. I wonder if anyone else have had this problem, and how you guys generally handle scroll restoration in React.
Note: all the movies are imported from a sampleMovies.js — so I'm not waiting for an API response in my example.
Note that history.scrollRestoration is just a way of disabling the browser's automatic attempts at scroll restoration, which mostly don't work for single-page apps, so that they don't interfere with whatever the app wants to do. In addition to switching to manual scroll restoration, you need some sort of library that provides integration between the browser's history API, React's rendering, and the scroll position of the window and any scrollable block elements.
After not being able to find such a scroll restoration library for React Router 4, I created one called react-scroll-manager. It supports scrolling to top on navigation to a new location (aka history push) and scroll restoration on back/forward (aka history pop). In addition to scrolling the window, it can scroll any nested element that you wrap in an ElementScroller component. It also supports delayed/asynchronous rendering by using a MutationObserver to watch the window/element content up to a user-specified time limit. This delayed rendering support applies to scroll restoration as well as scrolling to a specific element using a hash link.
npm install react-scroll-manager
import React from 'react';
import { Router } from 'react-router-dom';
import { ScrollManager, WindowScroller, ElementScroller } from 'react-scroll-manager';
import { createBrowserHistory as createHistory } from 'history';
class App extends React.Component {
constructor() {
super();
this.history = createHistory();
}
render() {
return (
<ScrollManager history={this.history}>
<Router history={this.history}>
<WindowScroller>
<ElementScroller scrollKey="nav">
<div className="nav">
...
</div>
</ElementScroller>
<div className="content">
...
</div>
</WindowScroller>
</Router>
</ScrollManager>
);
}
}
Note that an HTML5 browser (10+ for IE) and React 16 are required. HTML5 provides the history API, and the library uses the modern Context and Ref APIs from React 16.
How do you handle your scroll restoration?
Turns out browsers have implementations of the history.scrollRestoration.
Maybe you can use that? Check these links out.
https://developer.mozilla.org/en/docs/Web/API/History#Specifications
https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration
In addition, I found an npm module that might be able to handle scroll restoration in react with ease, but this library only works with react router v3 and below
https://www.npmjs.com/package/react-router-restore-scroll
https://github.com/ryanflorence/react-router-restore-scroll
I hope this can help.
my solution:save window.scrollY for every pathname with a Map (ES6)
scroll-manager.tsx
import { FC, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export function debounce(fn: (...params: any) => void, wait: number): (...params: any) => void {
let timer: any = null;
return function(...params: any){
clearTimeout(timer);
timer = setTimeout(()=>{
fn(...params)
}, wait);
}
}
export const pathMap = new Map<string, number>();
const Index: FC = () => {
const { pathname } = useLocation();
useEffect(() => {
if (pathMap.has(pathname)) {
window.scrollTo(0, pathMap.get(pathname)!)
} else {
pathMap.set(pathname, 0);
window.scrollTo(0, 0);
}
}, [pathname]);
useEffect(() => {
const fn = debounce(() => {
pathMap.set(pathname, window.scrollY);
}, 200);
window.addEventListener('scroll', fn);
return () => window.removeEventListener('scroll', fn);
}, [pathname]);
return null;
};
export default Index;
App.tsx
function App() {
return (
<Router>
<ScrollManager/>
<Switch>...</Switch>
</Router>
);
}
You can also use pathMap.size === 1 to determine if the user entered the app for the first time
I wound up using localStorage to track the scroll position - not sure this would handle all situations.
In this example, there's a Company page with a set of Stores, and each Store has a set of Display cases. I needed to track the scroll position of the display cases, so saved that to a 'storeScrollTop' key. There were 6 lines of code to add.
company.jsx:
// click on a store link
const handleClickStore = (evt) => {
window.localStorage.removeItem('storeScrollTop') // <-- reset scroll value
const storeId = evt.currentTarget.id
history.push(`/store/${storeId}`)
}
store.jsx:
// initialize store page
React.useEffect(() => {
// fetch displays
getStoreDisplays(storeId).then(objs => setObjs(objs)).then(() => {
// get the 'store' localstorage scrollvalue and scroll to it
const scrollTop = Number(window.localStorage.getItem('storeScrollTop') || '0')
const el = document.getElementsByClassName('contents')[0]
el.scrollTop = scrollTop
})
}, [storeId])
// click on a display link
const handleClickDisplay = (evt) => {
// save the scroll pos for return visit
const el = document.getElementsByClassName('contents')[0]
window.localStorage.setItem('storeScrollTop', String(el.scrollTop))
// goto the display
const displayId = evt.currentTarget.id
history.push(`/display/${displayId}`)
}
The trickiest part was figuring out which element had the correct scrollTop value - I had to inspect things in the console until I found it.
This component scroll to up if page is new but if page seen before restore the scroll.
scroll-to-top.tsx file:
import { useEffect, FC } from 'react';
import { useLocation } from 'react-router-dom';
const pathNameHistory = new Set<string>();
const Index: FC = (): null => {
const { pathname } = useLocation();
useEffect((): void => {
if (!pathNameHistory.has(pathname)) {
window.scrollTo(0, 0);
pathNameHistory.add(pathname);
}
}, [pathname]);
return null;
};
export default Index;
app.tsx file:
<BrowserRouter>
<ScrollToTop />
<App />
</BrowserRouter>
Use this library
https://www.npmjs.com/package/react-scroll-restoration
React Router does not provide out of the box support for scroll restoration and as it currently stands they won't either, because browsers are implementing some automatic scroll behavior

Resources