React Native - Component UI getting blocked after rerender - reactjs

I have two components that share a state through redux useSelector ().
CalendarDateList (with VirtualizedList) and Menu. In the Menu I have some buttons that change a seasonFilter status. This seasonFilter is used within the CalendarDateList to filter the array of the list. The problem is: when the button is pressed, the state changes and consequently the CalendarDateList rerender occurs, but this blocks the button's UI taking a long time so that it has the opacity of "pressed".
Is there any configuration to set the filter option of the other component's array without blocking the menu UI?
Basically I set through an action the seasonFilter on a Menu component and then access it on another List Component. When I hit the menu button, it blocks the UI thread stopping any animation from the TouchableOpacity button, for example. The UI is released only after the Calendar List is totally rendered.
export default function NavbarItem({ icon, text }) {
const seasonFilter = useSelector(state => state.seasonFilter);
const dispatch = useDispatch();
function toggleFilter(season) {
let seasonFilterTmp = seasonFilter.slice();
const seasonIndex = seasonFilter.indexOf(season);
seasonIndex === -1 ? seasonFilterTmp.push(season) : seasonFilterTmp.splice(seasonIndex, 1);
dispatch({ type: 'SET_FILTER', seasonFilter: seasonFilterTmp });
};
...
function CalendarTimeline() {
const calendarDates = useSelector(state => state.calendarDates);
const seasonFilter = useSelector(state => state.seasonFilter);
const filteredCalendarDates = calendarDates.filter(calendarDate => seasonFilter.includes(calendarDate.season))
return (
<Container >
<VirtualizedList
...

Related

React (Native) How can I re render my component when setstate receives same primitive value?

I have a modal in which a user selects to see more similar (lets say for simplicity products). If there is none, I set state to false and a noresults modal component is shown. the problem is when user AGAIN selects and there is again no products, the boolean is already set to false from before, so since no different value is provided for usestate, the component doesnt re render. However I want to show the modal popup with no results info every time. How can I make the component re render without using bad practises such a force update etc? Thank you
const App = ({route}) => {
const [more, setMore] = useState(true);
const findItem = (id) => { //triggered from a popup
const res = API.find...
if (res) {
setMore(true)
} else {
setMore(false)
}
}
return (
{!more ? <NoResults /> : <Results />}
)
}

Why React is rendering parent element, even if changed state isn't used in jsx? (Using React Hooks)

Im doing a React small training app using Hooks. Here's the example:
There is a MainPage.js and it has 3 similar child components Card.js. I have global state in MainPage and each Card has its own local state. Every Card has prop "id" from MainPage and clickButton func.
When I click button in any Card there are 2 operations:
Local variable 'clicked' becomes true.
The function from parent component is invoked and sets value to global state variable 'firstCard'.
Each file contains console.log() for testing. And when I click the button it shows actual global variable "firstCard", and 3x times false(default value of variable "clicked" in Card).
It means that component MainPage is rendered after clicking button ? And every Card is rendered too with default value of "clicked".
Why MainPage componenet is rendered, after all we dont use variable "firsCard", except console.log()?
How to make that after clicking any button, there will be changes in exactly component local state, and in the same time make global state variable "firstCard" changed too, but without render parent component(we dont use in jsx variable "firstCard")
Thanks for your help !
import Card from "../Card/Card";
const Main = () => {
const [cards, setCards] = useState([]);
const [firstCard, setFirstCard] = useState(null);
useEffect(() => {
setCards([1, 2, 3]);
}, []);
const onClickHandler = (id) => {
setFirstCard(id);
};
console.log(firstCard); // Showing corrrect result
return (
<div>
{cards.map((card, i) => {
return (
<Card
key={Date.now() + i}
id={card}
clickButton={(id) => onClickHandler(id)}
></Card>
);
})}
</div>
);
};
import React, { useState } from "react";
const Card = ({ id, clickButton }) => {
const [clicked, setClicked] = useState(false);
const onClickHandler = () => {
setClicked(true);
clickButton(id);
};
console.log(clicked); // 3x false
return (
<div>
<h1>Card number {id}</h1>
<button onClick={() => onClickHandler()}> Set ID</button>
</div>
);
};
export default Card;
You have wrong idea how react works.
When you change something in state that component will re render, regardless if you use that state variable in render or not.
Moreover, react will also re render all children of this component recursively.
Now you can prevent the children from re rendering (not the actual component where state update happened though) in some cases, for that you can look into React.memo.
That said prior to React hooks there was a method shouldComponentUpdate which you could have used to skip render depending on change in state or props.

Do the same fetch logic in different situation in one component

I have a page where there are some components: Filters, Pagination and List (with some data from the server)
Filter component includes some selects elements, inputs, etc. and button "Find".
Pagination component includes page switchers buttons and a selector for a direct page and a select to choose how many elements we should show on a page
List component shows data from the server API
Also, I have a Parent component that has all of them and a common state. (I will simplify fetch logic, but the main point that it is sent a request with 2 objects as data)
const reducer = (options, newOptions) => ({
...options,
...newOptions
})
const ParentComponent = () => {
const [filterOptions, setFilterOptions] = useReducer(
reducer,
{
filterOption1: "",
filterOption2: ""
....
}
)
const [pagingOptions, setPagingOptions] = useReducer(
reducer,
{
pageNumber: 1,
elementsPerPage: 10,
totalElement: 100
}
)
const doFetch = () => {
fetch('https://example.com', {
body: {filters: filterOptions, paging: pagingOptions}
})
}
return (
<>
<Filters
filterOptions={filterOptions}
onFilterOptionsChange={setFilterOptions}
onFindClick={doFetch}
/>
<Pagination
pagingOptions={pagingOptions}
onPagingOptionsChange={setPagingOptions}
/>
<List data={data} />
</>
)
}
When we change some filters, data in filterOptions changes because we put setFilterOptions dispatch in to Filters component (I did not show what is inside Filters component, the main point is that we use setFilterOptions({filterOption1: 'new_value'}) to change filterOptions when some filter select or input are changed) and when we click on the Find button inside Filters we use the method doFetch and do fetch with new values of filterOptions and default values of pagingOptions
After that, when data comes, we put it into List component and show it on a screen. Again I simplified it and write just <List data={data} />
My question is: How should I implement the logic for page changing. For Filters I have just one Find button to use fetch, but for paging, there are a lot of selectors and buttons that can change some of pagingOptions. And after we change a pagingOptions (a page or an elementsPerPage) I should immediately fire doFetch() method
I used useEffect
useEffect(() => {
doFetch()
}, [pagingOptions])
and see when pagingOptions changes then fire doFetch(), but eslint hooks warning told me that I should add doFetch in deps (I agree) but then it was a message in the console that doFetch should be in useCallback(), but if I wrapped doFetch() in useCallback() console told me that I should add filterOptions and pagingOptions in useCallback deps (because I use them in fetch) and after that, I got infinity loop of fetching api in network

React Ant Design Modal Method update on state change

I'm currently migrating to antd, and have a modal appear on a certain route (ie /userid/info). I'm able to achieve this if I use the antd Modal react component, but I'd like to be able to use the modal methods provided such as Modal.confirm,Modal.info and Modal.error as they offer nicer ui straight out of the box.
I'm running to multiple issues such as having the modal rendered multiple times (both initially and after pressing delete in the delete user case), and unable to make it change due to state (ie display loading bar until data arrives). This is what i've tried but it constantly renders new modals, ive tried something else but that never changed out of displaying <Loader /> even though isFetching was false. I'm not sure what else to try.
const UserInfoFC: React.FC<Props> = (props) => {
const user = props.user.id;
const [isFetching, setIsFetching] = React.useState<boolean>(true);
const [userInfo, setUserInfo] = React.useState<string>('');
const modal = Modal.info({
content: <Loader />,
title: 'User Info',
});
const displayModal = () => {
const renderInfo = (
<React.Fragment>
<p>display user.info</p>
</React.Fragment>
);
const fetchInfo = async () => {
try {
user = // some api calls
setUserInfo(user.info);
modal.update({ content: renderInfo })
} catch (error) {
// todo
}
setIsFetching(false);
};
fetchInfo();
};
displayModal();
return(<div />);
};
reference: https://ant.design/components/modal/#components-modal-demo-confirm
edit: here is a replication of one of the issues I face: https://codesandbox.io/embed/antd-reproduction-template-1jsy8
As mentioned in my comment, you can use a useEffect hook with an empty dependency array to run a function once when the component mounts. You can initiate an async call, wait for it to resolve and store the data in your state, and launch a modal with a second hook once the data arrives.
I made a sandbox here
Instead of going to /:id/info and routing to a component which would have returned an empty div but displayed a modal, I created a displayInfo component that displays a button and that controls the modal. I got rid of attempting to use routes for this.
What I have now is similar to the docs

How to check state changing between two components after button click?

I have 2 components for this task.First one is my wrapper component and the second one is component for buttons.
I am clicking one of the buttons of my button component and it changes state in my wrapper component.
Now how can i bring that whole action in Jest(Enzyme) environment?
it('should update state', () => {
const onButtonClickMock = jest.fn();
const wrapper = shallow(<MyComponent/>)
const buttons = shallow(<Colors onClick={onButtonClickMock} colors={['gray', 'black', 'white']}/>);
const d = buttons.find('div#buttons');
const b = d.find('button.btn');
b.at(1).simulate('click');//Pick second button from bunch and click
wrapper.update();
expect(onButtonClickMock).toHaveBeenCalledTimes(1);//This works
expect(component2.state(['selection'])).toBe(1);
})

Resources