import{useState } from 'react'
export const useForm=(callback, initialState={})=>{
const [values, setvalues] = useState(initialState)
const onChange=(event)=>{
setvalues({...values,[event.target.name]:event.target.value})
}
const onSubmit= event =>{
callback();
}
return{
onChange,
onSubmit,
values
}
}
function createPostCallback(){
createPost();
}
const [open, setOpen] =useState(false)
return (
<Modal
as={Form}
onSubmit={onSubmit}
this works fine but when i change this to {onSubmit&&setOpen(false)} it says too many renders, so i wrapped an arrow function like this
{()=> {onsubmit()&&setOpen(false)}} but only the onSubmit function
works and if i changed it to {()=> {setOpen(false)&&onSubmit()}} only
setOpen works..what am i doing here how to solve this !!
open={true}
dimmer={'blurring'}
onClose={() => setOpen(false)}
onOpen={() => setOpen(true)}
open={open}
trigger={<i class="circular add icon" ></i>}
>
<div>
<Image size='medium' src='/images/avatar/large/rachel.png' wrapped />
<TextArea
rows={2}
placeholder="whats on ur mind..?"
name="body"
onChange={onChange}
value={values.body}
>
</TextArea><Modal.Actions>
<Button type="submit" color="instagram">post</Button>
</Modal.Actions>
</div>
</Modal>
)
}
For the clean code i would recommend for you to do another function that call both functions:
function onSubmitAndSetOpenFalse() {
onSubmit();
setOpen(false);
}
...
onSubmit={onSubmitAndSetOpenFalse}
or you can do like Shri Hari told
onSubmit={() => { onSubmit(); setOpen(false); }}
Both will work.
I think you just need to call the function without the && operator.
onSubmit={() => { onSubmit(); setOpen(false); }}
Hope that works!
Related
I have the following component:
export default function SignIn() {
const [isLoading, setIsLoading] = useState(false);
const classes = useStyles();
const handleSubmit = (event) => {
event.preventDefault();
if(!AuthService.login(event.target))
{
setIsLoading(false);
}
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate onSubmit={(event) => { handleSubmit(event); setIsLoading(true); }}>
{ isLoading ?
<CircularProgress/> :
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
}
</form>
</div>
</Container>
);
}
When I submit the form, the "isLoading" becomes true, and the loading bars show up. Anyway if the "AuthService.login()" fails, the "isLoading" state is not updated.
What am I doing wrong?
Working code
The problem was related to the fact that
if(!AuthService.login(event.target))
is an async function, so I had to "await" for the response in order to evaluate it's result.
The working code:
async function handleSubmit (event) {
event.preventDefault();
setIsLoading(true)
try {
await AuthService.login(event.target);
setIsLoading(false)
} catch (e) {
console.log('Handle errors here');
} finally {
console.log('We do cleanup here');
}
}
Change:
onSubmit={(event) => { handleSubmit(event); setIsLoading(true); }}
to:
onSubmit={(event) => { handleSubmit(event) }}
Also change handleSubmit to:
const handleSubmit = (event) => {
event.preventDefault();
setLoading(true);
if(!AuthService.login(event.target))
{
setIsLoading(false);
}
}
you are always setting isLoading to true in onSubmit
export default function SignIn() {
const [isLoading, setIsLoading] = useState(false);
const classes = useStyles();
const handleSubmit = (event) => {
event.preventDefault();
setLoading(true)
if(!AuthService.login(event.target))
{
setIsLoading(false);
}
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate onSubmit={(event) => { handleSubmit(event) }}>
{ isLoading ?
<CircularProgress/> :
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
}
</form>
</div>
</Container>
);
}
Currently I have a certain navigation setup like this:
<div>
{letters?.map((letter) => {
return (
<button
type="button"
key={item.value}
data-active={value === item.value}
onClick={() => {
if (onChange) {
onChange(item.value);
}
}}
>
{item.label}
</button>
);
})}
</div>
I want to change it, to use Material UI tabs.
I have the following:
const MyComponent = ({ firstLetters }: Props) => {
const [value, setValue] = useState(0);
const handleChange = (_event: React.ChangeEvent<{}>, newValue: string) => {
console.warn(newValue);
setValue(newValue);
};
return (
<Tabs css={styles.tabsRoot} value={value} onChange={handleChange}>
{firstLetters?.map((item) => {
return (
<Tab
key={item.value}
label={item.label}
disableRipple
/>
);
})}
</Tabs>
)
};
How do I use onChange like my previous onClick function callback into the handleChange function?
According to the MUI docs, you should have value and onChange props in the Tabs component. So, you should remove the onClick prop from the Tab components, and add the aforementioned Tabs props like so:
<Tabs value={value} onChange={handleChange}>
{letters?.map((item) => {
return (
<Tab
key={item.value}
label={item.label}
disableRipple
value={item.value}
/>
);
})}
</Tabs>
i have an search function for searching in the table. Now. I want to use the search function on the onClick of the icon instead of the onChange of the input field. I don't think i need the throttle for that. I try to use the function setGlobalFilter directly inside the handleClick but it won't work
function GlobalFilter({ globalFilter, setGlobalFilter }) {
const [value, setValue] = React.useState(globalFilter)
const onChange = React.useCallback(
value => {
const throttledSetGlobalFilter = throttle(
value => {
setGlobalFilter(value || undefined)
},
2000
)
throttledSetGlobalFilter(value)
},
[setGlobalFilter]
)
return (
<span className={styles.componentGlobalFilter}>
<input
className={styles.input}
value={value || ''}
onChange={(e) => {
setValue(e.target.value)
onChange(e.target.value)
}}
placeholder={`Zoek in deze tabel`}
/>
<Icon onClick={handleClick} layoutClassName={styles.icon} {...{ icon }} />
</span>
)
function handleClick() {
}
}
If I understand your question, you want the icon click to invoke the setGlobalFilter callback with the input value. Remove the onChange handler and directly update value state. Call setGlobalFilter with the state value when the icon is clicked.
function GlobalFilter({ globalFilter, setGlobalFilter }) {
const [value, setValue] = useState(globalFilter ?? '');
const handleClick = () => setGlobalFilter(value);
return (
<span className={styles.componentGlobalFilter}>
<input
className={styles.input}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={`Zoek in deze tabel`}
/>
<Icon onClick={handleClick} layoutClassName={styles.icon} {...{ icon }} />
</span>
);
}
If you need to throttle the setGlobalFilter then the following may help.
function GlobalFilter({ globalFilter, setGlobalFilter }) {
const [value, setValue] = useState(globalFilter ?? '');
const handleClick = throttle(
() => setGlobalFilter(value),
2000,
);
return (
<span className={styles.componentGlobalFilter}>
<input
className={styles.input}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={`Zoek in deze tabel`}
/>
<Icon onClick={handleClick} layoutClassName={styles.icon} {...{ icon }} />
</span>
);
}
I have a component (Navigation.js) which imports another component (Dialog.js). I want that if I react to a click event, call a function in the dialog component (handleClickOpen ()). But I don't know how to do that. So what i have to do ?
Navigation.js
export default function SimpleBottomNavigation() {
return (
<BottomNavigation
value={value}
onChange={(event, newValue) => {
setValue(newValue);
}}
showLabels
className={classes.root}
>
<BottomNavigationAction
label="Home"
onClick={'HERE I WANT TO CALL THE FUNCTION IN THE DIALOG COMPONENT'}
icon={<RestoreIcon />}
/>
<BottomNavigationAction label="Neuer Plan" icon={<FavoriteIcon />} />
<BottomNavigationAction label="Azubis" icon={<LocationOnIcon />} />
</BottomNavigation>
);
}
Dialog.js
export default function CustomizedDialogs() {
const [open, setOpen] = React.useState(false);
*/THIS FUNCTION I WANT TO CALL FROM NAVIGATION.JS */
const handleClickOpen = () => {
setOpen(true);
};
[...]
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open dialog
</Button>
<Dialog
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
Modal title
</DialogTitle>
<DialogContent dividers>
</Dialog>
</div>
);
}
There is a aimple way to use child's functions, with functional components it's forwardRef and useImperativeHandle, along those lines:
Navigation (parent)
function Navigation() {
const dialogRef = useRef();
return(
<button onClick={() => dialogRef.current.handleClickOpen()}>
Click me!
</button>
);
}
Dialog (child)
const Dialog = forwardRef((props, ref) => {
useImperativeHandle(ref, () => {
const handleClickOpen = () => {
//your implementation
};
});
return (...);
});
If I over-simplified, please let me know :)
I'm not a React expert yet thus I have a question for you - how to invoke my popup window from:
import {options, columns,convertToArray} from './consts'
const index = () => {
const {data, loading, error, performFetch} = fetchHook({path: "/xxx/yyy", fetchOnMount: true})
return (
<div className={classes.Container}>
<h1>List of products</h1>
<Divider className={classes.Divider} />
<ProductTable data={convertToArray(data)} options={options} columns={columns}/>
</div>
)
}
export default index;
consts.js
export const actions = (productPropertyId, showModal) => {
const productDetails = (productPropertyId) => {
}
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
return (
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
>{"Remove"}
</Button>
</div>
)
};
export const convertToArray = (productList) => {
let products = []
if (productList != null) {
productList.map(product => {
column1, column2, column3, actions(product.id)]
products.push(prod)
})
}
return products;
};
My popup is --> <FormDialog/> based on react Materials.
Is it possible to invoke popup in this place?
I have a react material table with some columns. The last column contains 2 buttons, one of them is "Remove" (row). Here I want to invoke my popup. Maybe I should rebuild my structure?
UPDATE
Below is my popup - I wonder how to run this popup from the place above:
const formDialog = (popupOpen) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
{/*<Button variant="outlined" color="primary" onClick={handleClickOpen}>*/}
{/* Open alert dialog*/}
{/*</Button>*/}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default formDialog;
UPDATE 2
I updated my code taking into cosideration the tips from your response, see above. Can I add a parameter showModal in my export const actions = (productPropertyId, showModal) and then invoke this component with different showModal value? UNfortunately my popup doesn't appear when I click on Remove button :(
You can invoke it conditionally and controle it using some state variable. Like this:
const [removeModal, removeToggle] = useState(false);
return (<>
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
onClick={() => removeToggle(true)}
>{"Remove"}
</Button>
</div>
{removeModal ? <YourComponent /> : ''}
</>
)
I'm using a react fragment there <></> just to position the modal div outside the main div, but you can also invoke it inside your main div as well (I usually do this and set the position: fixed so the modal/popup coud appear in top of everything).