React component getting rerenderd on state change - javascript

My form was working perfectly earlier and now on after few changes the state change the input filed is loosing focus. I am using MUI and it was working earlier perfectly but suddenly it started losing focus after few minor change in state variable names. I think that is not the issue but i don't know why is this happening as all controlled forms are made like this i presume
import { useEffect, useState } from "react";
import Axios from "axios";
import { Schedule } from "./Schedule";
import { PageHeader } from "./PageHeader";
import { Button, TextField, styled, CircularProgress } from "#mui/material";
import FileUploadIcon from "#mui/icons-material/FileUpload";
import { DesktopDatePicker } from "#mui/x-date-pickers/DesktopDatePicker";
import { LocalizationProvider } from "#mui/x-date-pickers/LocalizationProvider/LocalizationProvider";
import { AdapterDayjs } from "#mui/x-date-pickers/AdapterDayjs";
import SendIcon from "#mui/icons-material/Send";
function Form() {
const [formData, setFormData] = useState({ startTime: "", endTime: "" });
const [file, setFile] = useState("");
const [data, setData] = useState("");
const [newValue, setValue] = useState(null);
const [loading, setLoading] = useState(false);
console.log(formData);
console.log("Rerendered");
function handleChange(event) {
setFormData((previousFormData) => {
return {
...previousFormData,
[event.target.name]: event.target.value,
};
});
}
function handleClick(event) {
setLoading(true);
event.preventDefault();
setTimeout(() => {
const postFormData = new FormData();
postFormData.append("startTime", formData.startTime);
postFormData.append("endTime", formData.endTime);
postFormData.append("date", newValue);
postFormData.append("file", file);
Axios.post("http://localhost:3001/aip", postFormData, {
headers: {
"Access-Control-Allow-Origin": "cors",
},
})
.then((res) => setData(res.data))
.then(setLoading(false))
.catch((err) => console.log(err));
}, 2000);
}
const Container = styled("main")({
marginTop: 50,
display: "grid",
justifyItems: "center",
});
const Div = styled("div")({
display: "flex",
gap: 50,
});
const Form = styled("form")({
border: "none",
width: "100%",
height: "100%",
margin: "auto",
display: "grid",
justifyItems: "center",
gap: 50,
});
return (
<>
<PageHeader />
<Container>
<Form onSubmit={handleClick} encType="multipart/form-data">
<Div class="timeInput">
<TextField
label="From"
name="startTime"
variant="outlined"
placeholder="9:00"
onChange={handleChange}
value={formData.startTime}
required
/>
<TextField
label="To"
name="endTime"
variant="outlined"
placeholder="9:30"
onChange={handleChange}
value={formData.endTime}
required
/>
</Div>
<div class="dateInput">
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
value={newValue}
label="Choose Date"
onChange={(newValue) => setValue(newValue)}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
</div>
<Div>
<Button sx={{ gap: 2 }} variant="contained" component="label">
Upload Schedule
<input
hidden
accept=".xlsx"
multiple
type="file"
onChange={(event) => {
const file = event.target.files[0];
setFile(file);
}}
/>
<FileUploadIcon />
</Button>
<Button sx={{ gap: 2 }} variant="contained" component="label">
Search
<input hidden type="submit" />
<SendIcon />
</Button>
</Div>
</Form>
</Container>
{loading ? (
<CircularProgress sx={{ alignItems: "center" }} />
) : (
<Schedule data={data} />
)}
</>
);
}
export default Form;

In this example, whenever the react state changes, App will re-render. And because App re-renders, Form gets re-declared and the internal state of the underlying DOM node (form) is lost.
export default function App() {
const [value, setValue] = useState("");
const Form = styled("form")({});
return (
<Form>
<input value={value} onChange={({ target }) => setValue(target.value)} />
</Form>
);
}
Declare Form outside of the body of App and it works
export default function App() {
const [value, setValue] = useState("");
return (
<Form>
<input value={value} onChange={({ target }) => setValue(target.value)} />
</Form>
);
}
const Form = styled("form")({});
If you must declare Form in the body of App (you probably don't need to), you can memoise it with React.useMemo
export default function App() {
const [value, setValue] = useState("");
const Form = useMemo(() => styled("form")({}), []);
return (
<Form>
<input value={value} onChange={({ target }) => setValue(target.value)} />
</Form>
);
}

You should move your styled components outside of the main Form component as they are causing re renders. Just note that you will then have clashing names. Styled Form and the actual component Form. So you will have to either rename the component or the styled Form. As to why this happens, I'm not entirely sure but ran into a similar issue before. I assume that every time you type and the state changes, it's re creating the styled components and then re rendering the main component.
// Imports
import { useEffect, useState } from "react";
import Axios from "axios";
import { Schedule } from "./Schedule";
import { PageHeader } from "./PageHeader";
import { Button, TextField, styled, CircularProgress } from "#mui/material";
import FileUploadIcon from "#mui/icons-material/FileUpload";
import { DesktopDatePicker } from "#mui/x-date-pickers/DesktopDatePicker";
import { LocalizationProvider } from "#mui/x-date-pickers/LocalizationProvi/LocalizationProvider";
import { AdapterDayjs } from "#mui/x-date-pickers/AdapterDayjs";
import SendIcon from "#mui/icons-material/Send";
// Styles
const Container = styled("main")({
marginTop: 50,
display: "grid",
justifyItems: "center",
});
const Div = styled("div")({
display: "flex",
gap: 50,
});
// Component
const Form = styled("form")({
border: "none",
width: "100%",
height: "100%",
margin: "auto",
display: "grid",
justifyItems: "center",
gap: 50,
});
function Form() {
// ...
}
export default Form;

Related

Axios not working correctly in React component

I have a page file in my React app that has a sidebar component.
import React, { Component } from 'react';
import Navbar from './../components/layout/Navbar';
import { Table } from './../components/layout/Table';
import { Sidebar } from './../components/layout/Sidebar';
export class LandingPage extends Component {
render() {
return (
<div>
<Navbar />
<h2 >Affiliation Information</h2>
<div className="wrapper">
<Table />
<Sidebar />
</div>
</div>
)
}
}
export default LandingPage
This sidebar component consists of an input textbox and a submit button. The idea here is that the user types something into the textbox (Ex: "La") triggering an event handler that sets the state of const interest then clicks submit. The submit button then plugs the interest state value into a url string for a call to my API using Axios, which is suppose to return the query results of any items where name is like "La". (Ex: [{"contact_name": "John Latham"}]).
import React, { useState, useEffect } from 'react';
import config from '../../config/config';
import axios from "axios";
export const Sidebar = () => {
const [interest, setInterest] = useState(null);
const [response, setResponse] = useState([]);
const [loadingData, setLoadingData] = useState(true);
console.log("At top: " + config.apiURL + `/affiliations/full?name=${interest}`);
function HandleChange(event) {
setInterest(event.target.value);
};
async function callYourAPI(interest) {
await axios
.get(config.apiURL + `/affiliations/full?name=${interest}`)
.then((res) => {
setResponse(res.data);
setLoadingData(false);
})
.catch(function (error) {
console.log(error);
});
};
useEffect(() => {
if (loadingData) {
callYourAPI();
} else {
console.log(response);
}
}, [loadingData, response]); // Makes the useEffect dependent on response.
return (
<div className="sidebar" style={{
borderLeft: "1px solid gray"
}}>
<h2 style={{ textAlign: "center" }}> Filter</h2>
<form>
<label style={{}}>Name</label> <br />
<input
placeholder="Type name"
style={{ textAlign: "center", marginRight: "10px", height: "25px", width: "200px" }}
value={interest}
onChange={HandleChange}></input>
<button className="btn btn-primary" onClick={() => callYourAPI(interest)}> Search</button>
</form>
</div >
)
}
However, what happens is that when I initially type in a value in the input box, its sets the state correctly, but when I click the button, everything goes haywire! The interest variable becomes undefined (the box gets cleared) and the process seems to loop through the Sidebar() function about 3 times ( and the sync callYourAPI() function) before it finishes but never returns the console.log(response); in my useEffect() function. At this point I am confused; what am I doing wrong here?!
UPDATE: I have made the recommended changes, using e.preventDefault() in my forms onSubmit. However, it seems like this is called on load, and its stuck in some kind of infinite call loop to the API.
export const Sidebar = () => {
const [interest, setInterest] = useState("");
const [response, setResponse] = useState([]);
const [loadingData, setLoadingData] = useState(true);
function HandleChange(event) {
setInterest(event.target.value);
};
async function callYourAPI(interest) {
await axios
.get(config.apiURL + `/affiliations/full?name=${interest}`)
.then((res) => {
setResponse(res.data);
setLoadingData(false);
})
.catch(function (error) {
console.log(error);
});
};
return (
<div className="sidebar" style={{
borderLeft: "1px solid gray"
}}>
<h2 style={{ textAlign: "center" }}> Filter</h2>
<form onSubmit={(e) => { e.preventDefault(); callYourAPI(interest); }}>
<label style={{}}>Name</label> <br />
<input
placeholder="Type name"
style={{ textAlign: "center", marginRight: "10px", height: "25px", width: "200px" }}
value={interest}
onChange={HandleChange}></input>
<button className="btn btn-primary"> Search</button>
</form>
</div >
)
}
Try adding event.preventDefault() like this:
function HandleChange(event) {
event.preventDefault()
setInterest(event.target.value);
};
The default behavior when submitting a form is to reload the page, therefore resetting the state. event.preventDefault() prevents the default behavior of the page.

Not getting pre-filled state value from redux when user try to edit the form?

I am trying to get the state from redux store and trying to fill the input field from state. If user in edit mode. In edit mode, we normally show the prefilled value in input field. But what is wrong with the below approach?
I am able to store single user successfully in reducer but in component i am not getting. Sometimes i get the value. Overall, it's very inconsistent.
import React, { useState, useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import { useSelector, useDispatch } from "react-redux";
import { useHistory, useParams } from "react-router-dom";
import { addUser, getSingleUser, updateUser } from "../redux/actions";
const useStyles = makeStyles((theme) => ({
root: {
marginTop: 100,
"& > *": {
margin: theme.spacing(1),
width: "45ch",
},
},
}));
const initialState = {
name: "",
address: "",
contact: "",
email: "",
};
const EditUser = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.users);
console.log("user", user);
const [state, setState] = useState(user);
const [error, setError] = useState("");
const { name, address, email, contact } = state;
const classes = useStyles();
const history = useHistory();
let dispatch = useDispatch();
const onInputChange = (e) => {
let { name, value } = e.target;
setState({ ...state, [name]: value });
};
useEffect(() => {
dispatch(getSingleUser(id));
}, []);
const handlSubmit = (e) => {
e.preventDefault();
console.log("name", name);
if (!name || !email || !address || !contact) {
setError("Please fill all Input Field");
} else {
dispatch(updateUser(state, id));
setError("");
history.push("/");
}
};
return (
<>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
type="submit"
color="secondary"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit user</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form
className={classes.root}
noValidate
autoComplete="off"
onSubmit={handlSubmit}
>
<TextField
id="standard-basic"
label="Name"
value={name}
name="name"
onChange={onInputChange}
type="text"
/>
<br />
<TextField
id="standard-basic"
value={email}
name="email"
label="Email"
type="email"
onChange={onInputChange}
/>
<br />
<TextField
id="standard-basic"
value={contact}
name="contact"
label="Contact"
type="number"
onChange={onInputChange}
/>
<br />
<TextField
id="standard-basic"
label="Address"
value={address}
name="address"
type="text "
onChange={onInputChange}
/>
<br />
<Button
style={{ width: "100px" }}
variant="contained"
type="submit"
color="primary"
>
Update
</Button>
</form>
</>
);
};
export default EditUser;
Below is redux actions logic to get the single user and dispatching an action to store single user value in reducer.
export const getSingleUser = (id) => {
return function (dispatch) {
axios
.get(`${process.env.REACT_APP_API}/${id}`)
.then((resp) => {
console.log("resp", resp);
dispatch(singleUser(resp.data));
})
.catch((error) => console.log(error));
};
};

Passing props to modal passes every object

I'm not 100% sure what's going on here. I've got a display component that displays a bunch of cards, using a map based on my database - On the card is an edit button that pops a modal up, passing props over to the edit form.. Here's kinda how it looks:
import React, { useState } from 'react'
import { useQuery, useMutation } from '#apollo/client'
import { GET_ALL_PROJECTS, REMOVE_PROJECT } from '../helpers/queries'
import { makeStyles } from '#material-ui/core/styles'
import DeleteIcon from '#material-ui/icons/Delete'
import EditIcon from '#material-ui/icons/Edit'
import AddForm from './AddForm'
import EditForm from './EditForm'
import AlertMessage from '../Alerts/AlertMessage'
import { Grid, Typography, Card, CardActionArea, CardActions, CardContent, CardMedia, Button, Modal, Backdrop, Fade } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const DisplayProjects = () => {
const styles = useStyles()
const [deleteItem] = useMutation(REMOVE_PROJECT)
const { loading, error, data } = useQuery(GET_ALL_PROJECTS)
const [status, setStatusBase] = useState('')
const [resultMessage, setResultMessage] = useState('')
const [addOpen, setAddOpen] = useState(false)
const [editOpen, setEditOpen] = useState(false)
const onDelete = (id, e) => {
e.preventDefault()
deleteItem({
variables: { id },
refetchQueries: [{ query: GET_ALL_PROJECTS }]
}).then(
res => handleSuccess(res),
err => handleError(err)
)
}
// Handles Result of the Delete Operation
const handleSuccess = (res) => {
console.log(res.data.deleteProject.proj_name)
// console.log('success!');
setResultMessage(res.data.deleteProject.proj_name)
setStatusBase({
msg: `Successfully Deleted ${resultMessage}`,
key: Math.random()
})
}
const handleError = (err) => {
console.log('error')
}
//Handles the Modal for Add Project
const handleAddOpen = () => {
setAddOpen(true);
};
const handleAddClose = () => {
setAddOpen(false);
};
//Handles the Modal for Edit Project
const handleEditOpen = () => {
setEditOpen(true);
};
const handleEditClose = () => {
setEditOpen(false);
};
if (loading) return '...Loading'
if (error) return `Error: ${error.message}`
return (
<div>
<div style={{ marginTop: 20, padding: 30 }}>
<Grid container spacing={8} justify='center' alignItems='center'>
{data.projects.map(p => {
return (
<Grid item key={p._id}>
<Card >
<CardActionArea>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CardMedia
style={{ width: 400, height: 100, paddingTop: 10, }}
component='img'
alt='Project Image'
height='140'
image={require('../../images/html-css-javascript-lg.jpg')}
/>
</div>
<CardContent >
<Typography gutterBottom variant='h5' component="h2">
{p.proj_name}
</Typography>
<Typography component='p'>
{p.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button>
<DeleteIcon onClick={e => onDelete(p._id, e)} />
</Button>
<Button onClick={handleEditOpen}>
<Modal
open={editOpen}
onClose={handleEditClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={editOpen}>
<div className={styles.paper}>
<EditForm
id={p._id}
close={handleEditClose}
name={p.proj_name}
desc={p.description}
gh={p.gh_link}
live={p.live_link}
img={p.image_url}
/>
</div>
</Fade>
</Modal>
<EditIcon />
</Button>
</CardActions>
</Card>
{ status ? <AlertMessage key={status.key} message={status.msg} /> : null}
</Grid>
)
}
)}
</Grid>
<Button type='button' onClick={handleAddOpen}>Add Project</Button>
<Modal
open={addOpen}
onClose={handleAddClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={addOpen}>
<div className={styles.paper}>
<AddForm close={handleAddClose} />
</div>
</Fade>
</Modal>
</div>
</div >
)
}
export default DisplayProjects
And here's the form. I've destructured out the props into variables and placed them into a state object called details, so they can be overwritten and submitted to the database..
import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import { useMutation, useQuery } from '#apollo/client'
import { EDIT_PROJECT, GET_ALL_PROJECTS, GET_PROJECT_BY_ID} from '../helpers/queries'
const AddForm = (props) => {
const params = useParams()
const id = params.toString()
// console.log(id);
const [editProjectItem] = useMutation(EDIT_PROJECT)
const {loading, data, error} = useQuery(GET_PROJECT_BY_ID, {
variables: {
id
},
})
const [details, setDetails] = useState({})
if (loading) return '...Loading';
if (error) return <p>ERROR: {error.message}</p>;
if (!data) return <p>Not found</p>;
setDetails(data.projectById)
console.log(data.projectById)
const submitForm = e => {
e.preventDefault()
try {
editProjectItem({
variables: { id, proj_name, description, gh_link, live_link, image_url},
refetchQueries: [{query: GET_ALL_PROJECTS}]
})
}
catch (err) {
console.log('You Goofed')
}
// setDetails({
// proj_name: '',
// description: '',
// gh_link: '',
// live_link: '',
// image_url: ''
// })
props.close()
}
const changeDetails = (e) => {
setDetails({
...details,
[e.target.name]: e.target.value
})
}
const {_id, proj_name, description, gh_link, live_link, image_url} = details
return (
<div key = {_id}>
<h2>Edit {proj_name}</h2>
<form onSubmit = {submitForm} >
<label>
Project Name:
<input
name = 'proj_name'
value = {proj_name}
onChange = {changeDetails}
/>
</label>
<label>Description</label>
<input
name = 'description'
value = {description}
onChange = {changeDetails}
/>
<label>GitHub Link</label>
<input
name = 'gh_link'
value = {gh_link}
onChange = {changeDetails}
/>
<label>Live Link</label>
<input
name = 'live_link'
value = {live_link}
onChange = {changeDetails}
/>
<label>Preview Image</label>
<input
name = 'image_url'
value = {image_url}
onChange = {changeDetails}
/>
<button type = 'submit'>Submit</button>
</form>
</div>
)
}
export default AddForm
The problem I'm running into, is that when I access the modal, the props are sent from literally EVERY Object, instead of the one, and displays the data for the last record instead of the one I want to edit You can see what happens here (I logged props.id in order to test) https://imgur.com/a/pcEKl89
What did I miss? (Disclaimer: I am still a student, and learning the craft.. be gentle on my code please)
EDIT: I just realized that I didn't indicate that this is the final form of the EditForm component. I haven't added the logic in to make the updates yet, I just wanted to get the data showing properly first.
EDIT2: I made some changes to how the ID is passed over, I was already using React-Router, so I went ahead and made a route to /edit/:id and then using useParams(), I got the ID that way. It seems to be working, however now I'm getting a Too many re-renders message. Updated the AddForm code above to reflect the changes..
I figured out the re-render issue.. it was as simple as dropping the setDetails function into a useEffect Hook:
useEffect(()=> {
if(data){
setDetails(data.projectById)
}
},[data])

React Modal selected data dissapears after it closes

I have the following problem my selected option dissapears after i close and reopen modal , someone told me this is because all data gets lost when a modal unmounts? So how do i cause the selected data to still show.
I am able to retrieve the selected data but all i want is the selected option to still be there once modal closes and reopens
Also My select component is not reusable any clue how i could optimize that piece of code
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import Backdrop from '#material-ui/core/Backdrop';
import Fade from '#material-ui/core/Fade';
import Button from '../Button/Button'
const useStyles = makeStyles(theme => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const TransitionsModal =(props) => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button clicked={handleOpen}></Button>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
{props.children}
</Fade>
</Modal>
</div>
);
}
export default TransitionsModal
Here is my select component
import React from 'react';
import InputLabel from '#material-ui/core/InputLabel';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
export default function NativeSelects(props) {
const [value, setValue] = React.useState('');
const handleChange = event => {
setValue(event.target.value);
console.log(event.target.value)
};
const data = props.list.map((item, id) => {
if (item.reciter_name_eng) {
return <option key={item.reciter_name_eng + id} value={item.id}>{item.reciter_name_eng} {item.style}</option>
}
//if its not null
if (item.name_simple) {
return <option key={item.name_simple + id} value={item.chapter_number}>{item.chapter_number}. {item.name_simple}</option>
}
if (item.language_name) {
return <option key={item.language_name + id} value={item.id}>{item.language_name} by {item.author_name}</option>
}
return null
})
return (
<div>
<FormControl variant="filled">
<InputLabel htmlFor="filled-age-native-simple">{props.type}</InputLabel>
<Select
native
value={value}
onChange={(event) => { props.changed(event.target.value, props.type); handleChange(event) }}>
<option value={null}> </option>
}
{data}
</Select>
</FormControl>
</div>
);
}
The problem is the default state of your Function Component is always an empty string i.e
const [value, setValue] = useState('');
When the modal is closed, the components are all unmounted, meaning when the modal is then reshown these are remounted and as a result the value will go back to an empty string.
You need to keep track of the selected value TransitionModel component and then pass this back into NativeSelects as a prop in order to set the default value i.e.
<NativeSelect defaultValue={...} />
Then in your component
function NativeSelect(props) {
const [value, setValue] = useState(props.defaultValue);
...
}

Setting the value of DatePicker (from antd) in react-hook-form

I'm trying to figure out how to use the DatePicker from antd with react-hook-form.
Currently, my attempt is:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import useForm from "react-hook-form";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import updateAction from "./updateAction";
import { Input as InputField, Form, Button, DatePicker, Divider, Layout, Typography, Skeleton, Switch, Card, Icon, Avatar } from 'antd';
import Select from "react-select";
const { Content } = Layout
const { Text, Paragraph } = Typography;
const { Meta } = Card;
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
const Team = props => {
const { register, handleSubmit, setValue, errors } = useForm();
const [ dueDate, setDate ] = useState(new Date());
const [indexes, setIndexes] = React.useState([]);
const [counter, setCounter] = React.useState(0);
const { action } = useStateMachine(updateAction);
const onSubit = data => {
action(data);
props.history.push("./ProposalBudget");
};
// const handleChange = dueDate => setDate(date);
const handleChange = (e) => {
setValue("dueDate", e.target.value);
}
const onSubmit = data => {
console.log(data);
};
const addMilestone = () => {
setIndexes(prevIndexes => [...prevIndexes, counter]);
setCounter(prevCounter => prevCounter + 1);
};
const removeMilestone = index => () => {
setIndexes(prevIndexes => [...prevIndexes.filter(item => item !== index)]);
};
const clearMilestones = () => {
setIndexes([]);
};
useEffect(() => {
register({ name: dueDate }); // custom register antd input
}, [register]);
Note: i have also tried name: {${fieldName}.dueDate - that doesn't work either.
return (
<div>
<HeaderBranding />
<Content
style={{
background: '#fff',
padding: 24,
margin: "auto",
minHeight: 280,
width: '70%'
}}
>
<form onSubmit={handleSubmit(onSubit)}>
{indexes.map(index => {
const fieldName = `milestones[${index}]`;
return (
<fieldset name={fieldName} key={fieldName}>
<label>
Title:
<input
type="text"
name={`${fieldName}.title`}
ref={register}
/>
</label>
<label>
Description:
<textarea
rows={12}
name={`${fieldName}.description`}
ref={register}
/>
</label>
<label>When do you expect to complete this milestone? <br />
<DatePicker
selected={ dueDate }
// ref={register}
InputField name={`${fieldName}.dueDate`}
onChange={handleChange(index)}
//onChange={ handleChange }
>
<input
type="date"
name={`${fieldName}.dueDate`}
inputRef={register}
/>
</DatePicker>
</label>
<Button type="danger" style={{ marginBottom: '20px', float: 'right'}} onClick={removeMilestone(index)}>
Remove this Milestone
</Button>
</fieldset>
);
})}
<Button type="primary" style={{ marginBottom: '20px'}} onClick={addMilestone}>
Add a Milestone
</Button>
<br />
<Button type="button" style={{ marginBottom: '20px'}} onClick={clearMilestones}>
Clear Milestones
</Button>
<input type="submit" value="next - budget" />
</form>
</Content>
</div>
);
};
export default withRouter(Team);
This generates an error that says: TypeError: Cannot read property 'value' of undefined
setValue is defined in handleChange.
I'm not clear on what steps are outstanding to get this datepicker functioning. Do I need a separate select function?
Has anyone figured out how to plug this datepicker in?
I have also tried:
const handleChange = (e) => {
setValue("dueDate", e.target.Date);
}
and I have tried:
const handleChange = (e) => {
setValue("dueDate", e.target.date);
}
but each of these generations the same error
I have built a wrapper component to work with external controlled component easier:
https://github.com/react-hook-form/react-hook-form-input
import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
function App() {
const { handleSubmit, register, setValue, reset } = useForm();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<RHFInput
as={<Select options={options} />}
rules={{ required: true }}
name="reactSelect"
register={register}
setValue={setValue}
/>
<button
type="button"
onClick={() => {
reset({
reactSelect: '',
});
}}
>
Reset Form
</button>
<button>submit</button>
</form>
);
}
try this out, let me know if it makes your life easier with AntD.
/* eslint-disable react/prop-types */
import React, { useState } from 'react';
import { DatePicker } from 'antd';
import { Controller } from 'react-hook-form';
import color from '../../assets/theme/color';
import DatePickerContainer from './DatePickerContainer';
function DatePickerAntd(props) {
const { control, rules, required, title, ...childProps } = props;
const { name } = childProps;
const [focus, setFocus] = useState(false);
const style = {
backgroundColor: color.white,
borderColor: color.primary,
borderRadius: 5,
marginBottom: '1vh',
marginTop: '1vh',
};
let styleError;
if (!focus && props.error) {
styleError = { borderColor: color.red };
}
return (
<div>
<Controller
as={
<DatePicker
style={{ ...style, ...styleError }}
size="large"
format="DD-MM-YYYY"
placeholder={props.placeholder || ''}
onBlur={() => {
setFocus(false);
}}
onFocus={() => {
setFocus(true);
}}
name={name}
/>
}
name={name}
control={control}
rules={rules}
onChange={([selected]) => ({ value: selected })}
/>
</div>
);
}
export default DatePickerAntd;
my container parent use react-hooks-form
const { handleSubmit, control, errors, reset, getValues } = useForm({
mode: 'onChange',
validationSchema: schema,
});
<DatePickerAntd
name="deadline"
title={messages.deadline}
error={errors.deadline}
control={control}
required={isFieldRequired(schema, 'deadline')}
/>
like that, its working for me ;-)
Try this:
<DatePicker
selected={ dueDate }
// ref={register}
InputField name={`${fieldName}.dueDate`}
onChange={()=>handleChange(index)}
//onChange={ handleChange }
>
<input
type="date"
name={`${fieldName}.dueDate`}
inputRef={register}
/>
It looks like if you are using onChange={handleChange(index)} it does not pass a function instead you are passing an execution result of that function.
And if you are trying to access event inside handleChange, you should manually pass if from binding scope otherwise, it will be undefined.
onChange={()=>handleChange(index, event)}

Categories

Resources