Keep scroll-y after rerender - reactjs

I have dropdown list with a lot of checkboxes, so this container has scroll. But when i click on any checkbox - it selects\deselects itself and then state changes.
So the problem is that after rerendering this container is back to the top. Is it possible to keep container's scroll after rendering without saving it to the state?

You can save the "snapshot" of the scroll position before the commit phase.
getSnapshotBeforeUpdate() shows kind of what you are looking for.
The documentation example saves the current scroll position within getSnapshotBeforeUpdate lifecycle method and then use the snapshot value passed to componentDidUpdate(prevProps, prevState, snapshot) as the last argument to restore the scroll position.
It doesn't require creating a state to save the scroll position as you requested.

It's happening because you are re-rendering the complete list of the checkboxes.
There are 2 possible approaches:
Re-render only relevant checkbox
Save the scroll position of the container and update it once component is re-rendered.
Unfortunately, you haven't added any code examples, so can't share the code changes.

Related

react-window saves state of deleted row

here is my problem.
I am using react-window to render large tables. Each row has its own local state. After I delete a row, the next row moves up and gains the state of the deleted row (this how it looks in my app).
Is there a workaround for this problem? Can i have local state for each row with react-window?
codesandbox example | gif how it works
There is a fundamental issue here - react-window will dynamically render, unmount and rerender components. So if you want data persisted across re-renders - you must pass the count via props and a function to change it. Store the data with the item data itself.
I will try and show a working demo of that soon ,but look at this to understand yet another problem happening with this code: https://codesandbox.io/s/react-window-row-state-problem-mmukt
I have edited the size to be 150 instead of 50, this leads to the window getting a scrollbar. Now, try and click on "two" multiple times. This will increase the count. Next, scroll to bottom and go back up. You'll see that the count is lost.
This is because of the way react-window mounts and unmounts components.
To fix this in your original code, I made the following modifications:
1. Move count/active state to parent.
2. Pass the updateCount function to TableRow.
Essentially, the TableRow is now a pure component :)
Here is the link: https://codesandbox.io/s/react-window-row-state-problem-uzsdl
Hope this helps.
In a nutshell, you can't. The issue is that, when you delete an item, the same component that was used previously for it will be used to render the item after it (so if you delete the 2nd item, the 3rd item will use the Row from the old 2nd element). It will maintain that item's state. If you add a useEffect to detect that the item changed, then you break everything after it (since everything is bumped up one, all of the items after the deleted item will reset their state. You don't have access to the sibling component's state, so you have no way to propagate the state.
You have a few options here:
Add the selection to the properties of the item, and provide a way to update the item.
Make the selection state part of the App component's state, and pass it down to the component so it can render appropriately.
I'll also add that you probably don't want to map over your presumably large list of items every render like you are now. It looks like you are doing this to expose deleteItems. I would recommend something more like this:
<FixedSizeList
height={500}
width={300}
itemCount={items.length}
itemSize={50}
itemData={{items, deleteItem}}
>
{Row}
</FixedSizeList>
Then your Row component's data will have both the items array and the deleteItem function. If you maintain your selected rows state in App, you easily extend this to pass the selected state and modification functions down to your Row component.

Prevent React hook from being called when state changes

I noticed that when you use useState in a hook and then use setState to change the value that is being cached, the hook will be called again and re-render the component. While this may be desired most of the time, I have one case where I don't want to re-render when the state changes. This case is when you have a navigation menu (tabs) at the top of the page and when you click on a tab, it shows content in a pane beneath it. I really only want to hide the content for the tab that is currently shown and then display the content for the tab that is selected. When content is hidden, this is essentially setting the css "display" style to "none". This is desirable to preserve the state of the content's pane and also avoid effects like retrieving data.
I can think of one solution to handle this but it does require splitting the components into isolated modules. I am curious though whether there is a way to change state but without having the side effect of a component being re-rendered.

React App Prevent Rerender of All Items in Grid When Redux Updates

I've got an app that shows a list of items in a grid. Some of the items have an embedded video which flashes or stops playing (if it's already playing) when it's rerendered.
The list is maintained in Redux. When the user scrolls to the bottom of the page it loads more results which causes the Redux state to update which, in turn, causes the grid to rerender all of the items plus the new items.
I'm looking for a solution that will just add more items to the end of the grid instead of rerendering everything.
I've got a component that holds the entire grid. It renders a component for each item in the grid. In each item I have a component that holds the embedded video.
Any ideas?
If each item in the grid is a component, like you said, you should use React.memo (for functional compoents) or Reat.PureComponent (for class components). It will prevent the component from rerendering if the props have not changed. I can't guarantee your videos will keep playing, but if the only reason they stop playing or flash is because the component is being rerendered then it should fix the problem.
Maybe this can help: when passing information from redux to your component, try to update the list of the objects instead of sending a new one
It's redux UpdateObject method.

Keep React Native Animated position on re-render

So I'm creating a screen on React native that has a button at the bottom end. When it's clicked it moves a searchbar from the bottom to the top of screen and everything that was on top of it is moved to the side out of screen view. Then it renders a ListView. So far so good. My problem is when a do my search that it receives data from api and populate ListView, as its re-rendering the component, everything is moved back to its initial place. I want to know if there's a way to re-render only my ListView, or keep the animated position after re-render.
One easy way to keep the animated position after re-render, is by setting a alreadyAnimated boolean value in the state of your component. Initialize this value to false and set it to true when calling the function that triggers your animation. Then, conditionally call the animation only if this.state.alreadyAnimated is false and only set the initial animated value/position of your searchbar if alreadyAnimated is false.

scrollToIndex doesn't scroll the list in React-virtualized list

With react-virtualized, when you scroll the list and set the same scrollToIndex, it doesn't scroll back to the same index.
Is there a way to reset the list back to the position?
That's because the prop hasn't changed. You can use the scrollToCell or scrollToRow method (depending on whether you're using Grid or List/Table) to re-scroll to the same index.
As #brianvaughn also said, you List doesn't scroll to the index back again because to the props to the react-virtualized list component haven't changed.
Since, By default all react-virtualized components use shallowCompare to avoid re-rendering unless props or state has changed. The components won't re-render until an update has happened.
The solution to this is to let react-virtualized know that something external has changed. This can be done a couple of different ways.
Also its mentioned in the documentation that you have two ways to let react-virtualized know that something has changed
Pass-thru props
The shallowCompare method will detect changes to any props, even if they aren't declared as propTypes. This means you can also pass through additional properties that affect cell rendering to ensure changes are detected. So you could pass additional props that change when you fire an event that for the virtualized list doesn't change anything.
<List
{...listProps}
sortBy={sortBy}
/>
Public methods
Grid and Collection components can be forcefully re-rendered using forceUpdate. For Table and List, you'll need to call forceUpdateGrid to ensure that the inner Grid is also updated.
So whenever you wish to update the scrollToIndex if, its value hasn't changed from the previous value you could call forceUpdateGrid on the List. This method can come in handy in a lot of other situations as well
Having said that, it may not be the best option always as there is another option to force scroll event if the scrollToIndex hasn't changed, which is the scrollToRow option. However it requires the row you want to scroll to be visible. Its its not, you won't be able to use it.
scrollToRow (index: number)
Ensure row is visible. This method can be used to safely scroll back
to a cell that a user has scrolled away from even if it was previously
scrolled to.

Resources