Reload user after deleting an item from it with React - javascript

Using React.Js When trying to delete users from the end of the list the deleted users are replaced by the last item in the list. Only on page refresh do the deleted users actually go away and the user list updates.The back-end is work because I can delete the user but I need to refresh to see a new update
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
const combinedStyles = combineStyles(popupStyles, UserPageStyles);
export default function RemoveUser(props) {
const global = useContext(GlobalContext);
const [open, setOpen] = React.useState(false);
let org = {}
if (global.state.selectedOrg && Object.keys(global.state.selectedOrg).length !== 0) {
org = global.state.selectedOrg
} else if (global.state.defaultOrg && Object.keys(global.state.defaultOrg).length !== 0) {
org = global.state.defaultOrg
}
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
props.handleClose();
};
const handleSubmit = async () => {
let result = await global.api.removeAccount({
id: props.account._id,
role: global.state.user && global.state.user.document.roles[0] || '',
accountRole: props.type === 'patients' ? 'patient' : props.type === 'providers' ? 'doctor' : props.type === 'moas' ? 'oa' : props.type === 'admins' ? 'healthOrgAdmin' : props.type,
org: org._id,
username: props.account.username,
});
if (result && result.status === 200) {
handleClose();
props.refresh();
} else {
alert('Unable to remove account.');
}
}
const classes = combinedStyles();
return (
<div>
<ButtonBase className={props.className} onClick={handleClickOpen}> <Typography className={classes.typography}>Remove</Typography></ButtonBase>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle className={classes.dialogTitle} id="alert-dialog-slide-title">Remove Account<IconButton onClick={handleClose} className={classes.dialogClose} children={<ClearIcon />} /> </DialogTitle>
<DialogContent>
<DialogContentText className={classes.contentText}>
Are you sure you want to remove {props.account.contact_name}'s account? You will not be able to revert this.
</DialogContentText>
</DialogContent>
<DialogActions className={classes.dialogAction}>
<Button onClick={handleClose} color="primary" className={classes.actionBtn}>
Cancel
</Button>
<Button onClick={handleSubmit} color="primary" className={classes.actionBtn}>
Yes
</Button>
</DialogActions>
</Dialog>
</div>
);
}

Usually after the delete request completes (with a 200) you would have to set some new state to trigger a re-render. Do you have a list of all the users in state somewhere? you need to do something like setUsers(users.filter(u => u.id !== deletedUserId))

There are many tricks to re-render your react components :
Re-render component when state changes
Any time a React component state has changed, React has to run the render() method. So you maybe you should affect your users list to a state so that when the list of users change (the state) it re-render your component.
Re-render component when props change
Re-render with key prop
Force a re-render by this.forceUpdate();
Take a look on that Methods to force a re-render in React

Related

I need to open and close accordion based on arrow click

I am using Material UI accordion my issue is if I click on the arrow accordion will get open but again I click on the arrow it will not get closed I need to set it when the user clicks on the arrow according will close and open based on the arrow click check code sandbox link for better understanding.
export default function ControlledAccordions() {
const [expanded, setExpanded] = React.useState(false);
// const handleChange = (panel) => (event, isExpanded) => {
// setExpanded(isExpanded ? panel : false);
// };
const handleChange = (pannel) => {
setExpanded(pannel);
};
const panaalData = ["panel1", "panel2", "panel3", "panel4"];
return (
<div>
{panaalData.map((value, i) => {
return (
<Accordion expanded={expanded === `panel${i}`}>
<AccordionSummary
expandIcon={
<ExpandMoreIcon
onClick={() => {
handleChange(`panel${i}`);
}}
style={{ cursor: "pointer" }}
/>
}
aria-controls="panel1d-content"
id="panel1d-header"
>
fdsfdsf
</AccordionSummary>
<AccordionDetails>dfdf</AccordionDetails>
</Accordion>
);
})}
</div>
);
}
Code SandBox Link
you need to reset panel in that case. You can do that in change handler.
const handleChange = (pannel) => {
setExpanded(expended === pannel ? '' : pannel);
};
when you click the already expanded panel, it just sets it to be expanded again.
you need to check whether the clicked panel is already expanded and if so collapse it instead of expanding it:
const handleChange = (pannel) => {
if (expanded === pannel) setExpanded(false);
else setExpanded(pannel);
};
Create another component called MyAccordian and keep toggling accordion logic in that component. That way you don't need to handle toggling for each and every component separately.
export default function ControlledAccordions() {
const panaalData = ["panel1", "panel2", "panel3", "panel4"];
return (
<div>
{panaalData.map((value, i) => {
return <MyAccordian value={value} />;
})}
</div>
);
}
const MyAccordian = ({ value }) => {
const [expanded, setExpanded] = React.useState(false);
return (
<Accordion expanded={expanded}>
<AccordionSummary
expandIcon={
<ExpandMoreIcon
onClick={() => {
setExpanded((prev) => !prev);
}}
style={{ cursor: "pointer" }}
/>
}
aria-controls="panel1d-content"
id="panel1d-header"
>
{value}
</AccordionSummary>
<AccordionDetails>{value}</AccordionDetails>
</Accordion>
);
};
Working Demo
export default function ControlledAccordions() {
// initial state, everything is closed,
const [expandedIndex, setExpandedIndex] = React.useState(-1);
// this should be handleClic
const handleChange = (index) => {
// in useState, current expandedIndex is passed as the argument
// whatever we return will be set as the expandedIndex
setExpandedIndex((currentIndex) => {
// if any box is open, currentIndex will be that index
// when I click on the open box, it will set the expandedIndex=-1
if (currentIndex === index) {
return -1;
} else {
// If I reached here, that means I am on a closed box
// when I click I swithc the expandedIndex to current box's index
return index;
}
});
};
const panaalData = ["panel1", "panel2", "panel3", "panel4"];
return (
<div>
{panaalData.map((value, i) => {
// when handleChange runs on AccordionSummary expandedIndex===i
// that means when i click on the current box, it will be open
const isExpanded = expandedIndex === i;
return (
<Accordion expanded={isExpanded}>
<AccordionSummary
onClick={() => handleChange(i)}
expandIcon={
// I dont know #mui/material too much.
// main question is "I need to open and close accordion based on arrow click"
<ExpandMoreIcon
onClick={() => handleChange(i)}
style={{ cursor: "pointer" }}
/>
}
aria-controls="panel1d-content"
id="panel1d-header"
>
{value}
</AccordionSummary>
<AccordionDetails
style={{ backgroundColor: "green" }}
>{`box index ${i} is open`}</AccordionDetails>
</Accordion>
);
})}
</div>
);
}
proof of work:
const handleChange = (pannel) => {
setExpanded(!pannel);
};

NextJS: sidebar routing - currently clicked link

I recently asked this question, and found help to make my sidebar links respond by displaying a component in the main div of my page.
Now I want to figure out how to use this where I have more than one option in my sidebar. I added library and tasks, but when I click library - it reveals the library component in the main div (I want this), but when I then click tasks, it reveals both library and tasks components. I only want the tasks component to show in that event.
What is the js command to setState to false if the link is not the most recently clicked link?
function Sidebar({ toggleLibrary, toggleTasks }) {
// const { me, loading } = useMe()
return (
<Stack spacing="1" >
<NavButton label="Tasks" fontWeight="normal" onClick={toggleTasks} />
<NavButton label="Deals" fontWeight="normal" onClick={toggleLibrary}/>
</Stack>
)
}
const DashBase = () => {
const isDesktop = useBreakpointValue({ base: false, lg: true })
// the menu component should initially be hidden
const [showLibrary, setShowLibrary] = React.useState(false)
const [showTask, setShowTask] = React.useState(false)
// state setter to switch between our `true` and `false` states
const toggleLibrary = () => setShowLibrary(!showLibrary)
const toggleTasks = () => setShowTask(!showTask)
const router = useRouter()
return (
<Flex
>
{isDesktop ? <Sidebar toggleLibrary={toggleLibrary} toggleTasks={toggleTasks} /> : <Navbar />}
<Container py="8" flex="none">
{showLibrary ? <Library /> : '' }
{showTask ? <Tasks /> : '' }
Currently the code has an independent state for each item you want to toggle. While you could enqueue state updates to toggle all other states false when a new item is toggled true, this is rather unmaintainable code.
If the goal is to only ever render a single active item then I'd suggest just using a single active state and set the value in a single callback passed to the Sidebar component.
Example:
const DashBase = () => {
const isDesktop = useBreakpointValue({ base: false, lg: true });
// nothing initially is active
const [showActive, setShowActive] = React.useState(null);
// state setter to switch between our `null` and "active" id states
const toggleActive = (key) => setShowActive(
active => active === key ? null : key
);
const router = useRouter()
return (
<Flex>
{isDesktop ? <Sidebar toggleActive={toggleActive} /> : <Navbar />}
<Container py="8" flex="none">
{showActive === 'library' && <Library />}
{showActive === 'tasks' && <Tasks />}
...
...
function Sidebar({ toggleActive }) {
return (
<Stack spacing="1" >
<NavButton
label="Tasks"
fontWeight="normal"
onClick={() => toggleActive("tasks")}
/>
<NavButton
label="Deals"
fontWeight="normal"
onClick={() => toggleActive("library")}
/>
</Stack>
);
}

Editing an input field among many in React

check this code please https://stackblitz.com/edit/react-koqfzp?file=src/Section.js
Everytime i add an item, i'm also adding an random number that i want to edit. The number is rendered at a MUI Text field component.
<TextField
type="number"
variant="standard"
aria-readonly={edit === true ? false : true}
value={edit === true ? value : numbers[i]}
onChange={(e) => setValue(e.target.value)}
/>
And the buttons are rendered based on edit's state, like this:
{edit === true ? (
<button onClick={() => saveEdit(i)}>Save</button>
) : (
<button onClick={() => setEdit(true)}>Edit</button>
)}
And it's working, i can edit it and rerender with the new value, the problem is that when i click the edit button, every field receives the new input value and the save button shows up in every field, though when i save it, it returns to it's original value. How i can do what i'm doing but to just one specific field?
The problem is that you are using setEdit as a boolean
You can define it as the index of the array to edit with -1 as starting value
const [edit, setEdit] = useState(-1)
...
{edit === i ? (
<button onClick={() => saveEdit(i)}>Save</button>
) : (
<button onClick={() => setEdit(i)}>Edit</button>
)}
I recommend creating another component Exemple:Item with it has it's own states edit,value states
And pass the needed props to it which are the value,numbers,saveEdit(index,newValue),removeItem(index) and also the index
saveEdit has to be changed in section by passing the index and the newValue
I hope this clear for you
add the map values in a component and add the states inside the component so you will have multiple states for each component not just 1
parent component
{section.items.map((item, i) => (
<Component key={i} item={item}/>
)}
child component
const Component = ({ section, addItem, removeItem}) => {
const [newItem, setNewItem] = useState('');
const [edit, setEdit] = useState(false);
const [value, setValue] = useState(0);
const [numbers, setNumbers] = useState(section.number);
const handleChange = (e) => {
setNewItem(e.target.value);
};
return (
<TextField
type="number"
variant="standard"
aria-readonly={edit === true ? false : true}
value={edit === true ? value : numbers[i]}
onChange={(e) => setValue(e.target.value)}
/>
)

How to handle two onClicks in the same component in React?

There are two custom popups with OK and Cancel.
When you click the OK button, a review is written, and it is a code that is sent to the server as a post request. Then, the confirmation button appears once more, and a pop-up indicating that the writing is complete is displayed to the user. My problem here is that the post request is sent, but I don't get a second popup stating that the writing is complete.
The picture above is a pop-up design I made.
This code is the parent component.
export async function getServerSideProps(context) {
const token = cookie.parse(context.req.headers.cookie).accessToken;
const reviewId = context.query.reviewId;
const viewLecture = await getViewLecture(reviewId, token);
return {
props: { token, reviewId, viewLecture },
};
}
const WriteMentee = ({ token, reviewId, viewLecture }) => {
const [reviewInfo, setReviewInfo] = useState([]);
const [modal, setModal] = useState(false);
const [confirm, setConfirm] = useState(false);
return (
<>
<section className={styles.contentSection}>
{modal ? (
<ModalWithBackground
setModal={setModal}
className={styles.modalHeight}
>
<ReviewModal
mainText={"후기 등록"}
subText={"작성한 후기를 등록하시겠습니까?"}
cancelBtn={setModal}
confirmBtn={async () => {
const res = await writeReviewAPI(
token,
reviewId,
content,
score
);
if (res == 200 || res == 201) {
console.log("200 || 201");
setConfirm(!confirm);
}
}}
/>
{confirm ? (
<ConfirmModal
mainText={"후기 등록"}
subText={`후기 등록이 완료되었습니다.\n수강하시느라 고생 많으셨습니다.`}
confirm={() => {
router.push("/mentee/mypage/menteeReview");
}}
/>
) : (
<></>
)}
</ModalWithBackground>
) : (
<></>
)}
</section>
</>
)
👇 This component is the component of the first popup image.
const ReviewModal = ({ mainText, subText, cancelBtn, setConfirm }) => {
return (
<section>
<article className={styles.roundModal}>
<h3 className={styles.title}>{mainText}</h3>
<p className={styles.subTitle}>{subText}</p>
<div className={styles.btnSection}>
<button className={styles.cancel} onClick={cancelBtn}>
<span>취소</span>
</button>
<button className={styles.check} onClick={() => setConfirm(false)}>
<span>확인</span>
</button>
</div>
</article>
</section>
);
};
👇 This component is the component of the second popup image.
const ConfirmModal = ({ mainText, subText, confirm }) => {
return (
<section>
<article className={styles.roundModal}>
<h3 className={styles.title}>{mainText}</h3>
<p className={styles.subTitle}>{subText}</p>
<div className={styles.confirmSection}>
<button className={styles.confirm} onClick={confirm}>
<span>확인</span>
</button>
</div>
</article>
</section>
);
};
I think you can use your confirm state to handle which content is showing.
<ModalWithBackground
setModal={setModal}
className={styles.modalHeight}
>
{confirm ? (
<ConfirmModal
mainText={"후기 등록"}
subText={`후기 등록이 완료되었습니다.\n수강하시느라 고생 많으셨습니다.`}
confirm={() => {
router.push("/mentee/mypage/menteeReview");
}}
/>
) : (
<ReviewModal
mainText={"후기 등록"}
subText={"작성한 후기를 등록하시겠습니까?"}
cancelBtn={setModal}
confirmBtn={async () => {
const res = await writeReviewAPI(
token,
reviewId,
content,
score
);
if (res == 200 || res == 201) {
console.log("200 || 201");
setConfirm(!confirm);
}
}}
/>
)}
</ModalWithBackground>
On your parent component, you are passing a confirmBtn prop, which you should be calling in the onClick on the first modal, and doesn't take any arguments. That should handle the opening of the other modal.
But in the ReviewModal, you call a different function called setConfirm and pass it false. You should just be calling confirmBtn (although the name might not matter if you're not using TS). But no need to pass it an argument since that's all handled by the parent component.
Just pass the onClick handler the method itself and change the props to take in confirmBtn instead of setConfirm.
<button className={styles.check} onClick={confirmBtn}>
<span>확인</span>
</button>
And then also double check the api call is returning 200 just in case!
I solved it by writing code like this 😄
{modal ? (
<ModalWithBackground
setModal={setModal}
className={styles.modalHeight}
>
<ReviewModal
mainText={"후기 등록"}
subText={"작성한 후기를 등록하시겠습니까?"}
cancelBtn={() => {
setModal(false);
}}
confirmBtn={async () => {
const res = await writeReviewAPI(
token,
reviewId,
content,
score
);
if (res == 200 || res == 201) {
console.log("200 || 201");
setConfirm(!confirm);
}
}}
/>
</ModalWithBackground>
) : (
<></>
)}
{confirm ? (
<ModalWithBackground
setModal={setModal}
className={styles.modalHeight}
>
<ConfirmModal
mainText={"후기 등록"}
subText={`후기 등록이 완료되었습니다.\n수강하시느라 고생 많으셨습니다.`}
confirm={() => {
router.push("/mentee/mypage/menteeReview");
}}
/>
</ModalWithBackground>
) : (
<></>
)}

How to create a load more button in Reactjs

I have cards and modals, I need to only show 2 cards in the page and have a button to show the rest, I’m new in programming and react, I don’t know what I have to do, that’s what I have now,
import React from "react"
import { Container } from "./_styles"
import { useTheme } from "#material-ui/core/styles"
import ImgMediaCard from "./../../../components/Cartao"
import AlertDialog from './../../../components/Modal'
export default function PortfolioSection(props) {
let arrayProjetos = props.projects;
const [selectedId, setSelectedId] = React.useState(0);
const [open, setOpen] = React.useState(false);
const handleClickOpen = (id) => {
setSelectedId(id);
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
let projetos = arrayProjetos.edges[selectedId].node.frontmatter;
return (
<Container>
{arrayProjetos.edges.map(
function criaCard(e, index){
let title = e.node.frontmatter.name;
let imageCard = e.node.frontmatter.images[0];
return (
<>
<ImgMediaCard
alt={title}
imagetitle={title}
texttitle={title}
src={imageCard}
click={() => handleClickOpen(index)}>
</ImgMediaCard>
</>
)
}
)}
<AlertDialog
imageModal={projetos.images[1]}
open={open}
handleClose={handleClose}
title={projetos.name}
text={projetos.description} />
</Container>
)
}
I'm using hooks to open the right modal when I click the "See more" button in the card, its working ok, I have 6 cards now, but I can add more in the future. I just need to limit how many cards I see when I enter the page and have a button to show everything.
API: you can add a pagination and total properties to your api call which returns 2 cards by default and you can handle the count of cards by increasing the pagination value. You may notice to add check to avoid situations like count > total.
UI: you can add const [cardCount, setCardCount] = useState(2)
and map through your cards array until the index not greater than cardCount value:
{arrayProjetos.edges.map((e, index) => { return index <= cardCount && <ImgMediaCard ... /> })}
<Box display={cardCount === 2 ? 'none' : 'block'}>
<Button
onClick={()=> arrayProjetos.edges.length - cardCount === 3 ? setCardCount(...cardCount - 1) : setCardCount(...cardCount - 2)}>See less</Button>
</Box>
<Box display={cardCount === arrayProjetos.edges.length ? 'none' : 'block'} >
<Button
onClick={() => arrayProjetos.edges.length - cardCount === 1 ? setCardCount(...cardCount + 1) : setCardCount(...cardCount + 2)}>See more </Button>
</Box>
How are you getting the cards?
You need to lazy load, if you are getting from a server, you can implement pagination, so the server sends back 2 cards every time (or based on a page data you send to server)
So every time you click the "load more" button, you fire a function who ask server for two more cards, and add the response to your cards js variable
Thanks Nazar, I did something similar I guess:
const [showMore, setShowMore] = React.useState(false);
const clickShow = () => {
setShowMore(oldValue => !oldValue);
showMore ? setButtonText("Ver Mais >") : setButtonText("Ver Menos <");
};
arrayProjetos.edges.slice(0, showMore ? arrayProjetos.edges.lenght : 2).map(
function criaCard(e, index){/*same thing here*/})
<button onClick={() => clickShow()}>{buttonText}</button>
I used slice to limit the array and a hook to change value

Categories