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
Related
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 />}
)
}
I have created cart functionality and I have managed this using useState hook. though I can change quantity of items but if I close the Modal, it can't reset. (I want quantity 0 on closing a Modal).
let [addonData, setAddonData] = useState([]);
Here if i open modal again after closing I get previous values, but I want 0 instead of that. Please check attached image for your reference.
I have tried but I got this error: too many re-renders. react limits the number of renders to prevent an infinite loop.
So, If anyone have idea then please let me know.
Thanks in advance :)
For doing that, you'll probably need to fire a reset function on modal dismiss, but what that means exactly?!
Parent Component
Imagine that we have this modal which receives a state from the parent component.
const MyParentComponent = () => {
const [opened, setOpened] = useState(false);
const [state, setState] = useState([])
const onOpen = () => {
setOpened(true)
}
const onDismiss = () => {
setOpened(false)
setState([])
}
return (
<>
{myComponent()}
<Modal isOpened={opened} state={state} setState={setState} onDismiss={onDismiss} />
</>
)
}
What is happening here is that we pass a state from the Parent to the Modal Component, but every time we close the modal we reset the state we were passing to it.
This solve your problem, but check this section of the beta.reactjs docs
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
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
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
...