Spinner not displaying on react component - javascript

I have a react component that display a card with input and button.
I want when I click the button to display a spinner, meanwhile after button click there is an axios post request to write some data on server. the problem is I don't see the spinnet after I click the button, I only see the message after my request done (2-3 sec after click)
function User(props) {
const [memo, setMemo] = useState('');
const [isUpdated, setIsUpdated] = useState(false);
const [spinner, setSpinner] = useState(false);
const onClickForm = (e) => {
e.preventDefault();
let inputValue = document.getElementById(inputId);
setSpinner(true);
axios.post(url, {
body: {
"internalid": props.internalid,
"memo": memo
},
headers: {"Content-Type": "application/json"},
redirect: 'follow'
}).then(response => {
console.log(response.data[0]);
setSpinner(false);
if (response.data[0].Success) {
setIsUpdated(true);
}
}, (error) => {
setIsUpdated(true);
setSpinner(false);
console.log(error)
}
)
setMemo('')
inputValue.value = '';
}
return (
<div key={props.index}>
<form onSubmit={e => onClickForm(e)}>
<Card className="card" key={props.index}>
<Card.Content>
<Card.Header>{props.tranid}</Card.Header>
<Card.Description>
{props.name}
</Card.Description>
<br/>
{!isUpdated ? (
<div>
<Input id={inputId} placeholder='Write to memo'
onChange={(e) => setMemo(e.target.value)}/>
<br/>
<button style={{marginTop: 20}} className="ui inverted blue button">Submit Change
</button>
</div>
) : (
<div>
{!spinner ? <p style={{color: 'green'}}>UPDATED!</p> : <CircularProgress/>}
</div>
)
}
)}
</Card.Content>
</Card>
</form>
</div>
)
}

It seems that as the conditions are set, when !isUpdated then <CircularProgress /> may not render, even though spinner is true.
In the conditional render output, perhaps try:
{!isUpdated && !spinner && (
<div>
<Input
id={inputId}
placeholder="Write to memo"
onChange={(e) => setMemo(e.target.value)}
/>
<br />
<button style={{ marginTop: 20 }} className="ui inverted blue button">
Submit Change
</button>
</div>
)}
{!isUpdated && spinner && <CircularProgress />}
{isUpdated && !spinner && <p style={{ color: "green" }}>UPDATED!</p>}
Or if ternary operator is preferred:
{isUpdated ? (
<p style={{ color: "green" }}>UPDATED!</p>
) : spinner ? (
<CircularProgress />
) : (
<div>
<Input
id={inputId}
placeholder="Write to memo"
onChange={(e) => setMemo(e.target.value)}
/>
<br />
<button style={{ marginTop: 20 }} className="ui inverted blue button">
Submit Change
</button>
</div>
)}

Related

Change state for each button individually

I have a button to show details of each card in my react project like this. the amount of cards are unknown and it is based on what I get from API.
just like this, I get the data and I'm mapping it:
<div className={styles.contentCardContainer}>
{factorList !== undefined && factorList.length > 0 ? (
factorList.map((list, i) => (
<div key={uuidv4()} className={styles.contentCard}>
<IconButton
color={showListService ? "error" : "success"}
onClick={() => {
setShowlistService((prev) => !prev);
HandleFetchDetails(list.ID);
}}
sx={{
display: "flex",
alignItems: "center",
alignSelf: "flex-start",
}}
>
{showListService ? <RemoveCircle /> : <AddCircle />}
</IconButton>
<section className={styles.eachSection}>
<img src={doctorImage} alt="doctor" />
<p>{list.Doctor}</p>
</section>
<section className={styles.eachSection}>
<img src={calendarImage} alt="doctor" />
<p>
{list.Date}   
{list.Time}
</p>
</section>
<section className={styles.eachSection}>
<img src={RialImage} alt="doctor" />
<p>{list.Payable}  ریال</p>
</section>
<section className={styles.eachSection}>
<img src={discountImage} alt="doctor" />
<p>{list.discount}</p>
</section>
{showListService && (
<DetailsListService listDetails={listDetails} styles={styles} />
)}
</div>
))
) : (
<Alert
variant="filled"
severity="warning"
sx={{ justifyContent: "space-between" }}
>
<p>There's no data</p>
</Alert>
)}
</div>
</div>
as you can see, I have IconButton element that I mapped it so each element have this button. but there's a problem.
I just have one state.
just like this :
const [showListService, setShowlistService] = React.useState(false);
since the amount cards I will receive is unknown. I can't define multiple states, because I don't know how many I should define. this is a bad practice too.
so whenever I click on IconButton, state changes for ALL buttons. how can I make it work individually.
change state for just a button in that specific element
Create a component for the cards and each card can keep the state:
factorList.map((factor, index) => {
return <Card key={index} factor={factor} />;
});
const Card = ({ factor }) => {
const [state, setState] = useState(false);
return <div>Your card stuff here</div>;
};

Transform button into text after condition is true

I'm using the AppBar component of material-ui library, I added a button, I'd say it's hardcoded in the AppBar, I'm trying to transform this button to just a text in the AppBar, but I got nothing.
How can I transform the button in the AppBar to just text?
App.js:
const connectWalletChecker = () => {
return(
<div>
<SearchAppBar id='walletButton' apptitle={apptitle} color='primary' variant='contained' text='Connect Wallet' handle={connectWalletPressed}/>
</div>
)
}
const AlreadyConnected = () => {
return(
<div>
<SearchAppBar id='walletButton' apptitle={apptitle} color='primary' variant='contained' text={walletAddress} handle={null}/>
</div>
)
}
return(
<div>
{walletAddress ? AlreadyConnected() : connectWalletChecker()}
</div>
)
};
AppBar:
<Button id={props.id} color={props.color} variant={props.variant}
href="#contained-buttons" onClick={props.handle}>{props.text}</Button>
Full code on Github.
use variant="text" instead of variant="contained" for transforming button into text.
your code will be :
const connectWalletChecker = () => {
return(
<div>
<SearchAppBar id='walletButton' apptitle={apptitle} color='primary' variant='text' text='Connect Wallet' handle={connectWalletPressed}/>
</div>
)
}
const AlreadyConnected = () => {
return(
<div>
<SearchAppBar id='walletButton' apptitle={apptitle} color='primary' variant='text' text={walletAddress} handle={null}/>
</div>
)
}
return(
<div>
{walletAddress ? AlreadyConnected() : connectWalletChecker()}
</div>
)
};

Formik Field Array field's id change when wrap with modal

I'm using Formik with chakra UI to create dynamic forms using the Fomrik Field array. and I Open my form in Chakra modal, problem is when I open my form in modal it change field name and id to array last object id , When i remove modal its works as expected, I Recreated my problem here
my code sample
<Box>
{/* start card grid */}
<Box py="12" px={{ base: "6", md: "0" }}>
<FormikProvider value={formik}>
<Box as="section" maxW={{ base: "xs", md: "full" }} mx="auto">
<form onSubmit={formik.handleSubmit}>
<SimpleGrid
columns={{ base: 1, sm: 1, md: 3, xl: 4 }}
spacing="6"
>
<FieldArray name="locations">
{() =>
formik.values?.locations?.map(
(props: any, index: string) => {
const { location, reviews } = props;
return (
<>
<Modal
isOpen={isOpen}
onClose={onClose}
>
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody >
<>
<FormLabel>Page URL</FormLabel>
<InputGroup size="md">
<InputLeftAddon
borderTopEndRadius={0}
borderBottomEndRadius={0}
borderColor="grayExtend.300"
bg="grayExtend.100"
fontSize="0.8rem"
px={2}
>
{modal.urlPrefix}
</InputLeftAddon>
<>
<Input
name={`locations.${index}.facebookUrl`}
value={
formik.values.locations[
index
].facebookUrl
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
placeholder="facebook name"
type="text"
borderWidth="1.5px"
fontSize="0.8rem"
borderColor="grayExtend.300"
borderTopStartRadius={0}
borderBottomStartRadius={
0
}
/>
<Box
fontSize="0.875rem"
color="red"
mt={2}
>
</Box>
</>
</InputGroup>
</>
</ModalBody>
<ModalFooter mb={4}>
<Button
key={`btn${
Math.random() * 1000
}`}
id={modal.web}
colorScheme="blue"
border="1px"
isFullWidth
type="submit"
_hover={{ bg: "#152aa3" }}
disabled={formik.isSubmitting}
>
Submit
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Flex
direction="column"
alignItems="center"
rounded="md"
padding="8"
position="relative"
shadow="base"
id={index}
>
<Box
position="absolute"
inset="0"
height="20"
roundedTop="inherit"
/>
<VStack
spacing="4"
mt={3}
>
{buttons.map(
({ title, id, icon }) => (
<Button
key={id+1}
id={id}
colorScheme="grayExtend.100"
borderWidth="1px"
borderColor="grayExtend.300"
variant="outline"
p="10px"
borderRadius="4px"
minH="60px"
minW="300px"
onClick={(e) =>
handleOpenModal(e)
}
className={
formik.values.locations[index]
.facebookUrl &&
formik.errors &&
formik.errors.locations &&
getIn(
formik.errors,
`locations.${index}.facebookUrl`
) &&
"success"
}
display="flex"
justifyContent="start"
bg="#f5faff"
fontSize="0.875rem"
>
{title}
</Button>
)
)}
</VStack>
</Flex>
</>
);
}
)
}
</FieldArray>
</SimpleGrid>
</form>
</Box>
</FormikProvider>
</Box>
{/* end card grid */}
</Box>
as I can see you create modal views for each field. This is not a bad solution but it is better to make one modal and give it data to display (I take out the modal component from the map cycle)
Now the main problem to set special data for each button. I add the state new field id.
In formik inputs change values and names to locations[${modal.id}].facebookUrl
The reason, why are you getting last modal is not controlling it's statement. There is now field which says React not to show 2nd,3nd or other components, so there are 4 modals on each other. You can also use your code but add condition which will hide or show your modal view for example
modal.id === idx && <Modal/>
Also link of this code
import {
Box,
useDisclosure,
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalOverlay,
SimpleGrid,
Flex,
VStack,
FormLabel,
Input,
InputGroup,
InputLeftAddon,
ModalFooter
} from "#chakra-ui/react";
import { FieldArray, getIn, useFormik, FormikProvider } from "formik";
import * as React from "react";
const Form = () => {
const [modal, setModal] = React.useState({
web: "",
urlPrefix: "",
id: 0
});
const { isOpen, onOpen, onClose } = useDisclosure();
const cardData: any = {
locations: [
{
facebookUrl: "fb 1"
},
{
facebookUrl: "fb2"
},
{
facebookUrl: ""
},
{
facebookUrl: ""
}
]
};
const buttons = [
{
id: "facebook",
title: "Facebook",
icon: ""
}
];
// modal func
const handleOpenModal = (e: any, idx: number) => {
const { id } = e.target;
if (id === "facebook") {
setModal({
...modal,
web: "facebook",
urlPrefix: "facebook.com",
id: idx
});
} else return null;
onOpen();
return null;
};
const formik = useFormik({
initialValues: cardData,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
}
});
return (
<Box>
{/* start card grid */}
<Box py="12" px={{ base: "6", md: "0" }}>
<FormikProvider value={formik}>
<Box as="section" maxW={{ base: "xs", md: "full" }} mx="auto">
<form onSubmit={formik.handleSubmit}>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody>
<>
<FormLabel>Page URL</FormLabel>
<InputGroup size="md">
<InputLeftAddon
borderTopEndRadius={0}
borderBottomEndRadius={0}
borderColor="grayExtend.300"
bg="grayExtend.100"
fontSize="0.8rem"
px={2}
>
{modal.urlPrefix}
</InputLeftAddon>
<>
<Input
name={`locations[${modal.id}].facebookUrl`}
value={
formik.values.locations[modal.id].facebookUrl
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
placeholder="facebook name"
type="text"
borderWidth="1.5px"
fontSize="0.8rem"
borderColor="grayExtend.300"
borderTopStartRadius={0}
borderBottomStartRadius={0}
/>
<Box fontSize="0.875rem" color="red" mt={2}></Box>
</>
</InputGroup>
</>
</ModalBody>
<ModalFooter mb={4}>
<Button
key={`btn${Math.random() * 1000}`}
id={modal.web}
colorScheme="blue"
border="1px"
isFullWidth
type="submit"
_hover={{ bg: "#152aa3" }}
disabled={formik.isSubmitting}
>
Submit
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<SimpleGrid
columns={{ base: 1, sm: 1, md: 3, xl: 4 }}
spacing="6"
>
<FieldArray name="locations">
{() =>
formik.values?.locations?.map(
(props: any, index: number) => {
const { facebookUrl } = props;
return (
<>
<Flex
direction="column"
alignItems="center"
rounded="md"
padding="8"
position="relative"
shadow="base"
id={index}
>
<Box
position="absolute"
inset="0"
height="20"
roundedTop="inherit"
/>
<VStack spacing="4" mt={3}>
{buttons.map(({ title, id, icon }) => (
<Button
key={id + 1}
id={id}
colorScheme="grayExtend.100"
borderWidth="1px"
borderColor="grayExtend.300"
variant="outline"
p="10px"
borderRadius="4px"
minH="60px"
minW="300px"
onClick={(e) => handleOpenModal(e, index)}
className={
formik.values.locations[index]
.facebookUrl &&
formik.errors &&
formik.errors.locations &&
getIn(
formik.errors,
`locations.${index}.facebookUrl`
) &&
"success"
}
display="flex"
justifyContent="start"
bg="#f5faff"
fontSize="0.875rem"
>
{title}
</Button>
))}
</VStack>
</Flex>
</>
);
}
)
}
</FieldArray>
</SimpleGrid>
</form>
</Box>
</FormikProvider>
</Box>
{/* end card grid */}
</Box>
);
};
export default Form;

Ant Design modal appears few times in react js

Ant modal appears few times, but if i remove map function inside the modal then its works fine its appears only one time.
what may be the issue? please help me to solve this issue (Modal triggering/appears few times)
Ant modal code follows:
const [visibleVoucherModal, setVisibleVoucherModal] = useState(false);
const showVoucherModal = () => {
setVisibleVoucherModal(true)
}
const closeVoucherModal = () => {
setVisibleVoucherModal(false);
};
const SavedVoucherModal = () => {
return (
<Modal title="Select Any Voucher" visible={visibleVoucherModal} onCancel={closeVoucherModal} footer={null} >
{savedVoucherList.length >= 1 ?
<div>
<Checkbox.Group className="w-100" onChange={checkBox} value={voucherVal} >
{savedVoucherList.map((item, i) => {
return (
<div key={i}>
<Checkbox value={item.Id} className="w-100" onChange={e => { handleCheckBox(e, i) }}
checked={checked[i] || true} disabled={!checked[i] && disabled} >
<div className="pl-4">
<List.Item style={{ marginTop: "-35px" }} className="py-0">
<List.Item.Meta
// avatar={<img src={item.Photo == '' ? 'd' : item.Photo} alt="" width="72" />}
title={<span style={{ fontSize: "12px" }}>{item.Name}</span>}
description={<div className="small">Expiry Date: <br />{item.ExpireDate.slice(0, 10)}</div>}
/>
<div className="pt-1">
<p className="font-weight-bold text-primary mb-0">{numberFormat(item.Price)}</p>
<small><del>{numberFormat(item.OldPrice)}</del></small>
</div>
</List.Item>
</div>
</Checkbox>
<Divider className="m-0 p-0 mb-4" />
</div>)
})}
</Checkbox.Group>
</div> :
<p className="mb-4">Oops, there is no voucher applicable to this order</p>
}
<Button type="primary" className="font-weight-bold btn-round" onClick={closeVoucherModal}>Close</Button>
</Modal>
)
}
Button:
<Button type="default" className="text-primary border-right-0 border-left-0" size="large" onClick={showVoucherModal}>Redeem Voucher</Button>

React Hooks : Change state of multiple items

I have a list of icons.
When I click on one of them, I want it to :
1/ change state to day or night
2/ change icon image to a sun or a moon.
But with the code that I've written, if I click on one, it changes all the icons image and only one state is linked to all.
How can I do so each icons has it own state and when i click on icon, only the icon clicked changes to a moon or a sun and not all of them ?
My code :
export default function MyModal() {
const [isVisible, setVisible] = useState(false);
const [isDay, setDay] = useState(true);
const { RangePicker } = DatePicker;
const { Option } = Select;
const style = {
verticalAlign: "middle",
marginRight: 10,
};
function handleDayNight() {
isDay === true ? setDay(false) : setDay(true);
console.log("isDay =", isDay);
}
function handleSelectChange(value) {
console.log(`selected ${value}`);
}
function handleCheckChange(checkedValues) {
console.log("checked =", checkedValues);
}
return (
<div>
<Button type="primary" onClick={() => setVisible(true)}>
Add an exception
</Button>
<Modal
title="Add an exception"
style={{ top: 20 }}
visible={isVisible}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
>
<p>Exceptions name</p>
<Input placeholder="80% Wednesday" />
<p style={{ marginTop: 10 }}>Select date</p>
<RangePicker onChange={([date]) => console.log(date)} />
<p style={{ marginTop: 10 }}>Frequency</p>
<Select
defaultValue="Weekly"
style={{ width: 120 }}
onChange={handleSelectChange}
>
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</Select>
<Divider />
<Checkbox.Group style={{ width: "100%" }} onChange={handleCheckChange}>
<div>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Monday">Monday</Checkbox>
<br></br>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Tuesday">Tuesday</Checkbox>
<br></br>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Wednesday">Wednesday</Checkbox>
<br></br>
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
<Checkbox value="Thursday">Thursday</Checkbox>
<br></br>
<Checkbox value="Friday">Friday</Checkbox>
<br></br>
<Checkbox value="Saturday">Saturday</Checkbox>
<br></br>
<Checkbox value="Sunday">Dimanche</Checkbox>
</div>
</Checkbox.Group>
</Modal>
</div>
);
}
Use a separate component for each of those, so you can have individual separate states inside those components. Eg, replace all of
{isDay === true ? (
<Sun style={style} onClick={handleDayNight} />
) : (
<Moon style={style} onClick={handleDayNight} />
)}
with
<SunMoon style={style} />
const SunMoon = ({ style }) => {
const [isDay, setDay] = useState(true);
const handleDayNight = () => setDay(!isDay);
return isDay
? <Sun style={style} onClick={handleDayNight} />
: <Moon style={style} onClick={handleDayNight} />;
};

Categories