React Datepicker - Uncaught RangeError: Invalid time value - reactjs

Building a simple ToDo list app in ReactJS. Below is my Add Task functional component:
import React, { useState } from "react";
import TaskDataService from "../services/task.service";
import DatePicker from 'react-datepicker'
import FadeIn from 'react-fade-in';
import "react-datepicker/dist/react-datepicker.css";
import 'bootstrap/dist/css/bootstrap.min.css';
const AddTask = () => {
const initialTaskState = {
id: null,
title: "",
description: "",
completed: false,
startDate: new Date()
};
const [task, setTask] = useState(initialTaskState);
const [submitted, setSubmitted] = useState(false);
const handleInputChange = event => {
const { name, value } = event.target;
setTask({ ...task, [name]: value });
};
const saveTask = () => {
var data = {
title: task.title,
description: task.description,
startDate: task.startDate
};
TaskDataService.create(data)
.then(response => {
setTask({
id: response.data.id,
title: response.data.title,
description: response.data.description,
completed: response.data.completed,
startDate: response.data.startDate
});
setSubmitted(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const newTask = () => {
setTask(initialTaskState);
setSubmitted(false);
};
return (
<FadeIn>
<div className="submit-form">
{submitted ? (
<div>
<h4>Task submitted successfully!</h4>
<button className="btn btn-success" onClick={newTask}>
Add
</button>
</div>
) : (
<div>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
required
value={task.title}
onChange={handleInputChange}
name="title"
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<input
type="text"
className="form-control"
id="description"
required
value={task.description}
onChange={handleInputChange}
name="description"
/>
</div>
<div className="form-group">
<label htmlFor="startDate">Start Date</label>
<DatePicker
selected={ task.startDate }
onChange={date => handleInputChange({target: {value: date.toISOString().split("T")[0], name: 'startDate'}})}
name="startDate"
dateFormat="yyyy-MM-dd"
/>
</div>
<button onClick={saveTask} className="btn btn-success">
Submit
</button>
</div>
)}
</div>
</FadeIn>
);
}
export default AddTask;
When I try to select a date from the Datepicker calendar, app rendering crashes. There are a few errors but it looks like the main one is "Uncaught RangeError: Invalid time value". However, the task is successfully added to the database and I can view it upon reloading the app. But for whatever case, it crashes upon submit.
In contrast, this is my standalone Task component which also contains code for editing an existing task. In that component, everything works 100%. I can open the Datepicker calendar on the selected task, select a new date, and submit it succesfully with zero problems:
import React, { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import TaskDataService from "../services/task.service";
import DatePicker from 'react-datepicker';
import FadeIn from 'react-fade-in';
const Task = props => {
const { id } = useParams();
let navigate = useNavigate();
const initialTaskState = {
id: null,
title: "",
description: "",
completed: false,
startDate: new Date(),
};
const [currentTask, setCurrentTask] = useState(initialTaskState);
const [message, setMessage] = useState("");
const getTask = id => {
TaskDataService.get(id)
.then(response => {
setCurrentTask(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
if (id)
getTask(id);
}, [id]);
const handleInputChange = event => {
const { name ,value } = event.target;
setCurrentTask({ ...currentTask, [name]: value });
};
const updateCompleted = status => {
var data = {
id: currentTask.id,
title: currentTask.title,
description: currentTask.description,
completed: currentTask.completed,
startDate: currentTask.startDate
};
TaskDataService.update(currentTask.id, data)
.then(response => {
setCurrentTask({ ...currentTask, completed: status });
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const updateTask = () => {
TaskDataService.update(currentTask.id, currentTask)
.then(response => {
console.log(response.data);
setMessage("The task was updated successfully!");
})
.catch(e => {
console.log(e);
});
};
const deleteTask = () => {
TaskDataService.remove(currentTask.id)
.then(response => {
console.log(response.data);
navigate("/tasks");
})
.catch(e => {
console.log(e);
});
};
return (
<FadeIn>
<div>
{currentTask ? (
<div className="edit-form">
<h4>Task</h4>
<form>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
value={currentTask.title}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<input
type="text"
className="form-control"
id="description"
value={currentTask.description}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div className="form-group">
<label htmlFor="startDate">Start Date</label>
<DatePicker
onChange={date => handleInputChange({target: {value: date.toISOString().split("T")[0], name: 'startDate'}})}
name="startDate"
dateFormat="yyyy-MM-dd"
value={currentTask.startDate.toString().split("T")[0]}
/>
</div>
</form>
{currentTask.completed ? (
<button
className="badge badge-primary mr-2"
onClick={() => updateCompleted(false)}
>
Mark Pending
</button>
) : (
<button
className="badge badge-primary mr-2"
onClick={() => updateCompleted(true)}
>
Mark Complete
</button>
)}
<button
className="badge badge-danger mr-2"
onClick={deleteTask}
>
Delete
</button>
<button
type="submit"
className="badge badge-success"
onClick={updateTask}
>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</FadeIn>
);
}
export default Task;
Done a lot of testing and research but no dice. Any ideas?

Commenter Konrad Linkowski provided the clue to the solution. I removed the line "startDate: response.data.startDate" and now it all works. I do not fully understand, so if anyone wants to explain the answer then please feel free.

Related

Diffrent Input type using Bootstrap desing in Redux-React functional componnent

Im trying to display my data in diffrent Form inputs, but in some of them, i dont know how to reach my data.
//DashShowDetails.js:
import { useState, useEffect } from 'react';
import { Button, Form } from 'react-bootstrap';
import { Input, Label } from 'reactstrap';
function DashShowDetails(props) {
const { data } = props;
const [edit, setEdit] = useState(false);
const [formData, setFormData] = useState({
id: data._id,
showId: data.showId,
showName: data.name,
showGenres: data.genres,
showStatus: data.status,
showPremiered: data.premiered,
showEnded: data.ended,
showRating: data.rating,
showSummery: data.summary,
showImage: data.image,
});
const onClick = () => {
setEdit(current => !current)
}
const onChange = (e) => {
setFormData((prevState) => ({
...prevState,
[e.target.name]: e.target.value
}))
}
const onSubmit = async (e) => {
e.preventDefault()
let show = formData;
dispatch(updateDashShow(show));
setEdit(current => !current);
setFormData((prevState) => ({
...prevState,
}))
}
useEffect(() => {
}, [edit]);
return (
<div>
<div>
<h5>{data.name}</h5>
<Button onClick={onClick}>Edit Show</Button>
</div>
<div>
{edit === true && (
<div>
<h3>Edit {data.name}</h3>
<Form onSubmit={onSubmit} >
<div>
<Label>Show ID:</Label>
<Input type="text" name="showId" value={data.showId} onChange={onChange} />
</div>
<div>
<Label>Show Name:</Label>
<Input type="text" name="showName" defaultValue={data.name} onChange={onChange} />
</div>
<div>
<Label>Show Genres:</Label>
<Input type="text" name="showGenres" defaultValue={data.genres} onChange={onChange} />
</div>
<div>
<Label>Show Status:</Label>
<Input type="select" name="showStatus" select={data.status} onChange={onChange}>
<option value="running">Running</option>
<option value="ended">Ended</option>
</Input>
</div>
<div>
<Label>Premiered Date:</Label>
<Input type="date" name="showPremiered" value={data.premiered} onChange={onChange} />
</div>
<div>
<Label>Ended Date:</Label>
<Input type="date" name="showEnded" value={data.ended} onChange={onChange} />
</div>
<div>
<Label>Show Rating:</Label>
<Input type="array" name="showRating" value={data.rating} onChange={onChange} />
</div>
<div>
<Label>Show Summery:</Label>
<Input type="textarea" name="showSummery" value={data.summery} onChange={onChange} />
</div>
<div>
<Label>Image:</Label>
</div>
<Button type="submit">Update Show</Button>
<Button>Delete Show</Button>
</Form>
</div>
)}
</div>
</div>
);
}
export default DashShowDetails;
My output when i click 'Edit Show' is:
How can i display my Data in inputs: Premiered Date, Show Rating, Show Summery & Image?
My Schema file in Node.js: (sending new data to MoongoDB)
const showSchema = new mongoose.Schema({
showId: {type: Number},
name: {type: String},
genres: {type: Array},
status: {type: String},//Boolean
premiered: {type: Date},
ended: {type: Date},
rating: {type: Array},
summary: {type: String},
image: {type: Array},//Link?File?
})
module.exports = mongoose.model('shows', showSchema);

Two-way data binding on large forms

(After working through a React.js tutorial, I'm currently coding my first little app to get more practice. So this will be a newbie question and the answer is certainly out there somewhere, but apparently I don't know what to search for.)
Google lists a lot of examples on how to achieve two-way data binding for one input field. But what about large, complex forms, possibly with the option of adding more dynamically?
Let's say my form consists of horizontal lines of input fields. All lines are the same: First name, last name, date of birth and so on. At the bottom of the table, there is a button to insert a new such line. All this data is stored in an array. How do I bind each input field to its respective array element, so that the array gets updated when the user edits a value?
Working example with two lines of two columns each:
import { useState} from 'react';
function App() {
var [name1, setName1] = useState('Alice');
var [score1, setScore1] = useState('100');
var [name2, setName2] = useState('Bob');
var [score2, setScore2] = useState('200');
function changeNameHandler1 (e) {
console.log(e)
setName1(e.target.value)
}
function changeScoreHandler1 (e) {
setScore1(e.target.value)
}
function changeNameHandler2 (e) {
setName2(e.target.value)
}
function changeScoreHandler2 (e) {
setScore2(e.target.value)
}
return (
<div>
<table>
<tbody>
<tr>
<td><input name="name1" id="id1" type="text" value={name1} onChange={changeNameHandler1} /></td>
<td><input name="score1" type="text" value={score1} onChange={changeScoreHandler1} /></td>
</tr>
<tr>
<td><input name="name2" type="text" value={name2} onChange={changeNameHandler2} /></td>
<td><input name="score2" type="text" value={score2} onChange={changeScoreHandler2} /></td>
</tr>
</tbody>
</table>
{name1} has a score of {score1}<br />
{name2} has a score of {score2}<br />
</div>
);
}
export default App;
How do I scale this up without having to add handler functions for hundreds of fields individually?
You can still store your fields in an object and then just add to the object when you want to add a field. Then map through the keys to display them.
Simple example:
import { useState } from 'react'
const App = () => {
const [fields, setFields ] = useState({
field_0: ''
})
const handleChange = (e) => {
setFields({
...fields,
[e.target.name]: e.target.value
})
}
const addField = () => setFields({
...fields,
['field_' + Object.keys(fields).length]: ''
})
const removeField = (key) => {
delete fields[key]
setFields({...fields})
}
return (
<div>
{Object.keys(fields).map(key => (
<div>
<input onChange={handleChange} key={key} name={key} value={fields[key]} />
<button onClick={() => removeField(key)}>Remove Field</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
Here is what I think you are trying to achieve in your question:
import { useState } from 'react'
const App = () => {
const [fieldIndex, setFieldIndex] = useState(1)
const [fields, setFields ] = useState({
group_0: {
name: '',
score: ''
}
})
const handleChange = (e, key) => {
setFields({
...fields,
[key]: {
...fields[key],
[e.target.name]: e.target.value
}
})
}
const addField = () => {
setFields({
...fields,
['group_' + fieldIndex]: {
name: '',
score: ''
}
})
setFieldIndex(i => i + 1)
}
const removeField = (key) => {
delete fields[key]
setFields({...fields})
}
return (
<div>
{Object.keys(fields).map((key, index) => (
<div key={key}>
<div>Group: {index}</div>
<label>Name:</label>
<input onChange={(e) => handleChange(e, key)} name='name' value={fields[key].name} />
<label>Score: </label>
<input onChange={(e) => handleChange(e, key)} name='score' value={fields[key].score} />
<button onClick={() => removeField(key)}>Remove Field Group</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
You may want to keep the index for naming in which case you can use an array. Then you would just pass the index to do your input changing. Here is an example of using an array:
import { useState } from 'react'
const App = () => {
const [fields, setFields ] = useState([
{
name: '',
score: ''
}
])
const handleChange = (e, index) => {
fields[index][e.target.name] = e.target.value
setFields([...fields])
}
const addField = () => {
setFields([
...fields,
{
name: '',
score: ''
}
])
}
const removeField = (index) => {
fields.splice(index, 1)
setFields([...fields])
}
return (
<div>
{fields.map((field, index) => (
<div key={index}>
<div>Group: {index}</div>
<label>Name:</label>
<input onChange={(e) => handleChange(e, index)} name='name' value={field.name} />
<label>Score: </label>
<input onChange={(e) => handleChange(e, index)} name='score' value={field.score} />
<button onClick={() => removeField(index)}>Remove Field Group</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
have multiple solutions to this problem for example:
Solution #1
Using useRef to store value of the field
import { useRef, useCallback } from "react";
export default function App() {
const fullNameInputElement = useRef();
const emailInputElement = useRef();
const passwordInputElement = useRef();
const passwordConfirmationInputElement = useRef();
const formHandler = useCallback(
() => (event) => {
event.preventDefault();
const data = {
fullName: fullNameInputElement.current?.value,
email: emailInputElement.current?.value,
password: passwordInputElement.current?.value,
passwordConfirmation: passwordConfirmationInputElement.current?.value
};
console.log(data);
},
[]
);
return (
<form onSubmit={formHandler()}>
<label htmlFor="full_name">Full name</label>
<input
ref={fullNameInputElement}
id="full_name"
placeholder="Full name"
type="text"
/>
<label htmlFor="email">Email</label>
<input
ref={emailInputElement}
id="email"
placeholder="Email"
type="email"
/>
<label htmlFor="password">Password</label>
<input
ref={passwordInputElement}
id="password"
placeholder="Password"
type="password"
/>
<label htmlFor="password_confirmation">Password Confirmation</label>
<input
ref={passwordConfirmationInputElement}
id="password_confirmation"
placeholder="Password Confirmation"
type="password"
/>
<button type="submit">Submit</button>
</form>
);
}
or still use useState but store all values in one object
import { useState, useCallback } from "react";
const initialUserData = {
fullName: "",
email: "",
password: "",
passwordConfirmation: ""
};
export default function App() {
const [userData, setUserData] = useState(initialUserData);
const updateUserDataHandler = useCallback(
(type) => (event) => {
setUserData({ ...userData, [type]: event.target.value });
},
[userData]
);
const formHandler = useCallback(
() => (event) => {
event.preventDefault();
console.log(userData);
},
[userData]
);
return (
<form onSubmit={formHandler()}>
<label htmlFor="full_name">Full name</label>
<input
id="full_name"
placeholder="Full name"
type="text"
value={userData.fullName}
onChange={updateUserDataHandler("fullName")}
/>
<label>Email</label>
<input
id="email"
placeholder="Email"
type="email"
value={userData.email}
onChange={updateUserDataHandler("email")}
/>
<label htmlFor="password">Password</label>
<input
id="password"
placeholder="Password"
type="password"
value={userData.password}
onChange={updateUserDataHandler("password")}
/>
<label htmlFor="password_confirmation">Password Confirmation</label>
<input
id="password_confirmation"
placeholder="Password Confirmation"
type="password"
value={userData.passwordConfirmation}
onChange={updateUserDataHandler("passwordConfirmation")}
/>
<button type="submit">Submit</button>
</form>
);
}
Solution #2
Or you have multiple libraries that also provide solutions like react-form-hook https://react-hook-form.com/

React-Hook-Form Validation

I'm looking to implement the form validation using react-hook. However, I'm facing some trouble in doing so as I've also added some stuff on my own under the handleSubmit and I'm not really sure how to go about it.
export default function Contact() {
const [message, setMessage] = useState(false);
const [alert, setAlert] = useState(true);
const { register, errors} = useForm();
const [showElement, setShowElement] = React.useState(false);
const handleSubmit = (e) => {
e.preventDefault();
emailjs.sendForm('', '', e.target, '')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
e.target.reset();
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
const onSubmit= data=>{
console.log(data);
}
return (
<div className="right">
<h2>Contact Me</h2>
<form onSubmit={handleSubmit} id="contactform">
<input type="text" placeholder="Name" name="name" ref={register({required: true, minLength: 2})}
required />
<button type="submit">Send</button>
</form>
{showElement ? (
<div className="submitmsg">
{message && (
<span> Messaged received. I'll respond to your query ASAP! </span>
)}
</div>
) : (
<div> </div>
)}{" "}
</div>
)
}
Thank you!
React hook form provides the handeSubmit method that receives the form data after validations. Also, you must use the errors object to show errors in the UI.
Here is the sandbox link: https://codesandbox.io/s/exciting-dust-df5ft?file=/src/App.js
I have updated your code accordingly:
import { useState } from "react";
import React from "react";
import emailjs from "emailjs-com";
import { useForm } from "react-hook-form";
export default function Contact() {
const [message, setMessage] = useState(false);
const {
register,
handleSubmit,
formState: { errors }
} = useForm();
const [showElement, setShowElement] = React.useState(false);
const onSubmit = (data) => {
emailjs
.send(
"service_t1ccrgq",
"template_gmmcyzr",
data,
"user_d0vUwhmqvbIYhEsyZF8tu"
)
.then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
return (
<div className="contact" id="contact">
<div className="left">
<img className="contactme" src="asset/email.gif" />
</div>
<div className="right">
<h2>Contact Me</h2>
<form onSubmit={handleSubmit(onSubmit)} id="contactform">
<input
type="text"
placeholder="Name"
name="name"
{...register("name", {
required: "Name is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<input
type="tel"
placeholder="Mobile Number"
name="mobile"
{...register("mobile", {
required: "Mobile Number is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<input
type="text"
placeholder="Email"
name="email"
{...register("email", {
required: "Email is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<textarea
placeholder="Message"
required
name="message"
{...register("message", {
required: "Message is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
></textarea>
<button type="submit">Send</button>
</form>
{showElement ? (
<div className="submitmsg">
{message && (
<span> Messaged received. I'll respond to your query ASAP! </span>
)}
</div>
) : (
<div> </div>
)}{" "}
</div>
{errors.name && (
<div>
<span>{errors.name.message}</span>
</div>
)}
{errors.message && (
<div>
<span>{errors.message.message}</span>
</div>
)}
{errors.email && (
<div>
<span>{errors.email.message}</span>
</div>
)}
{errors.mobile && (
<div>
<span>{errors.mobile.message}</span>
</div>
)}
</div>
);
}
You have to first initialize handleSubmit as below.
const {handleSubmit} = useForm();
Then in the form, onSubmit should be as below.
<form onSubmit={handleSubmit(onSubmit)}>
"onSubmit" is the method that is used to write the code in submitting form.
Regards.
In your code, it should be as below.
const onSubmit = (e) => {
e.preventDefault();
emailjs.sendForm('', '', e.target, '')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
e.target.reset();
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};

Is it possible Preload a form with Firestore values?

I have a form that uploads its values to the Firestore database, and would like to use the same component for updating the values, so the question might really be - how to load initial state according to a conditional whether the props are passed?
The form
import Servis from "./funkc/servisni";
import React, { useState } from "react";
export default function ContactUpdate(props) {
//
console.log(props.item);
//
const initialState = {
id: props.item.id,
ime: props.item.Ime,
prezime: props.item.Prezime,
imeError: "",
prezimeError: "",
date: props.item.Datum,
kontakt: props.item.Kontakt,
kontaktError: "",
published: true,
};
const [theItem, setTheItem] = useState(initialState);
const [imeError, setImeError] = useState();
const [prezimeError, setPrezimeError] = useState();
const [message, setMessage] = useState();
const { item } = props;
if (theItem.id !== item.id) {
setTheItem(item);
}
const handleInputChange = (event) => {
const { name, value } = event.target;
setTheItem({ ...theItem, [name]: value });
};
const updatePublished = (status) => {
Servis.update(theItem.id0, { published: status })
.then(() => {
setTheItem({ ...theItem, published: status });
setMessage("The status was updated successfully!");
})
.catch((e) => {
console.log(e);
});
};
const updateItem = () => {
let data = {
Ime: theItem.ime,
Prezime: theItem.prezime,
Kontakt: theItem.kontakt,
Datum: theItem.date,
published: true,
};
Servis.update(theItem.id, data)
.then(() => {
setMessage("The tutorial was updated successfully!");
})
.catch((e) => {
console.log(e);
});
};
const deleteItem = () => {
Servis.remove(theItem.id)
.then(() => {
props.refreshList();
})
.catch((e) => {
console.log(e);
});
};
const validate = () => {
let imeError = "";
let kontaktError = "";
if (!theItem.ime) {
imeError = "obavezan unos imena!";
}
if (!theItem.kontakt) {
imeError = "obavezan unos kontakta!";
}
if (kontaktError || imeError) {
this.setState({ kontaktError, imeError });
return false;
}
return true;
};
return (
<div className="container">
{theItem ? (
<div className="edit-form">
<h4>Kontakt</h4>
<form>
<div className="form-group">
<label htmlFor="ime">Ime</label>
<input
type="text"
className="form-control"
// id="title"
name="ime"
value={theItem.Ime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="Prezime">Prezime</label>
<input
type="text"
className="form-control"
// id="description"
name="Prezime"
value={theItem.Prezime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="Datum">Datum</label>
<input
type="text"
className="form-control"
// id="description"
name="Datum"
value={theItem.Datum}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="Kontakt">Kontakt</label>
<input
type="text"
className="form-control"
// id="description"
name="Kontakt"
value={theItem.Kontakt}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
{theItem.published ? "Published" : "Pending"}
</div>
</form>
{theItem.published ? (
<button onClick={() => updatePublished(false)}>UnPublish</button>
) : (
<button onClick={() => updatePublished(true)}>Publish</button>
)}
<button onClick={deleteItem}>Delete</button>
<button type="submit" onClick={updateItem}>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Please click on a Tutorial...</p>
</div>
)}{" "}
</div>
);
}
The component passing the props:
import React, { useState, useEffect } from "react";
import firebase from "./firebase";
import { Link } from "react-router-dom";
export default function FireDetail({ match }) {
console.log(match);
console.log(match.params.id);
const [item, setItem] = useState([]);
const [loading, setLoading] = useState();
const getIt = () => {
setLoading(true);
const docRef = firebase
.firestore()
.collection("polja")
.doc(match.params.id)
.get()
.then((doc) => {
setItem(doc.data());
});
//
console.log(docRef);
//
// const docRef = firebase.firestore().collection("polja").doc("documentId")
//
setLoading(false);
};
useEffect(() => {
getIt();
}, [match]);
if (loading) {
return <h3>samo malo...</h3>;
}
return (
<div className="container">
<div>
{console.log("item: ", item)}
Kontakt: tip - email
<p> {item.Kontakt}</p>
</div>
<div>
<p>Datum rodjenja: {item.Datum}</p>
{item.Prezime} {item.Ime}
</div>
<Link to={`/kontakt/update/${item.id}`}> ajd </Link>
</div>
);
}
Or you might have an alternative idea on how to solve the problem?
indeed it is
export default function ContactUpdate(props) {
const initialState = {
ime: props.item.Ime,
prezime: props.item.Prezime,
datum: props.item.Datum,
kontakt: props.item.Kontakt,
id: props.Id,
};
const [theItem, setTheItem] = useState();
const [message, setMessage] = useState();
useEffect(() => {
setTheItem(props.item);
}, []);
const handleInputChange = (event) => {
const { name, value } = event.target;
setTheItem({ ...theItem, [name]: value });
console.log(theItem);
};
const updateItem = (theItem) => {
let data = {
Ime: theItem.Ime,
Prezime: theItem.Prezime,
Kontakt: theItem.Kontakt,
Datum: theItem.Datum,
};
console.log(updateItem());
Servis.update(theItem.id, data)
.then(() => {
setMessage("Uspjesno ste izmijenili unos!");
})
.catch((e) => {
console.log(e);
});
};
const deleteItem = () => {
Servis.remove(theItem.id).catch((e) => {
console.log(e);
});
};
return (
<div className="container">
{console.log(("theItem", props, theItem))}
{theItem ? (
<div className="edit-form">
<h4>Kontakt</h4>
<form>
<div className="form-group">
<label htmlFor="ime">Ime</label>
<input
type="text"
className="form-control"
name="Ime"
value={theItem.Ime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="prezime">Prezime</label>
<input
type="text"
className="form-control"
name="Prezime"
value={theItem.Prezime}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="datum">Datum</label>
<input
type="text"
className="form-control"
name="Datum"
value={theItem.Datum}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label htmlFor="kontakt">Kontakt</label>
<input
type="text"
className="form-control"
name="Kontakt"
value={theItem.Kontakt}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<label>
<strong>Status:</strong>
</label>
</div>
</form>
<button onClick={deleteItem}>Delete</button>
<button type="submit" onClick={updateItem}>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Odaberi jedan broj...</p>
</div>
)}{" "}
</div>
);
}

React Apollo: How to access element Trix-Editor within a Mutation component?

Please, I need help accessing a <trix-editor> element in my Mutation component. I'm using React hook useRef() to access this, but getting null. From Chrome Dev Tools (image below) I can see the element has been referred. I have tried some of the solutions given here & here but with no luck. Problem seems to be in the rendering of the Mutation component. I'm fairly new to React, so not sure if I'm getting this right. What am I doing wrong? Thank you in advance for your help.
My code:
const EditProfile = () => {
const trixInput = useRef(null);
const [state, setState] = useState({
firstName: "",
lastName: "",
bio: "",
avatar: "",
gender: "",
country: ""
});
let userID
let { username } = useParams();
let userFromStore = JSON.parse(sessionStorage.getItem('_ev')));
let history = useHistory();
useEffect(() => {
// trixInput.current.addEventListener("trix-change", event => {
console.log(trixInput.current); // <<<< this prints null to the screen
// })
},[]);
if (userFromStore !== username) {
return (
<div className="alert">
<span className="closebtn" onClick={() => history.push("/console")}>×</span>
<strong>Wanning!</strong> Not authorized to access this page.
</div>
);
}
return (
<Query query={GetAuthUser}>
{({ loading, error, data }) => {
if (loading) return "loading...";
if (error) return `Error: ${error}`;
if (data) {
let {userId, firstName, lastName, avatar, bio, gender, country} = data.authUser.profile;
userID = parseInt(userId);
return (
<Mutation mutation={UpdateUserProfile}>
{(editProfile, { loading }) => (
<div className="card bg-light col-md-8 offset-md-2 shadow p-3 mb-5 rounded">
<article className="card-body">
<form onSubmit={ e => {
e.preventDefault();
editProfile({
variables: {
userId: userID,
firstName: state.firstName,
lastName: state.lastName,
bio: state.bio,
avatar: state.avatar,
gender: state.gender,
country: state.country
}
}).then(({ data: { editProfile: { success, errors } } }) => {
success ? alert(success) : alert(errors[0]["message"]);
});
}}
>
<div className="form-row mb-3">
<div className="col">
<input
type="text"
name="firstName"
placeholder="First name"
className="form-control"
defaultValue={firstName}
onChange={e => setState({firstName: e.currentTarget.value})}
/>
</div>
<div className="col">
<input
type="text"
name="lastName"
placeholder="Last name"
className="form-control"
defaultValue={lastName}
onChange={e => setState({lastName: e.currentTarget.value})}
/>
</div>
</div>
<div className="form-group">
<label className="">Bio</label>
<input
type="hidden"
defaultValue={bio}
name="bio"
id="bio-body"
/>
// <<< having trouble accessing this ref element
<trix-editor input="bio-body" ref={trixInput}/>
</div>
<input type="submit" className="btn btn-primary" value="Update Profile" disabled={loading}/>
</form>
</article>
</div>
)}
</Mutation>
);
}
}}
</Query>
)
}
export default EditProfile;
UPDATE:
For anyone interested, problem was solved by extracting Mutation component to a different file. Reason been Mutation component wasn't mounting on render, only the Query component was mounting. First iteration of the solution is shown as an answer.
EditProfile component
import React, { useState } from 'react';
import { Query } from 'react-apollo';
import { useHistory, useParams } from "react-router-dom";
import { GetAuthUser } from './operations.graphql';
import ProfileMutation from './ProfileMutation'
const EditProfile = (props) => {
const [state, setState] = useState({
firstName: "",
lastName: "",
bio: "",
avatar: "",
gender: "",
country: ""
});
let { username } = useParams();
let userFromStore = JSON.parse(sessionStorage.getItem('_ev'));
let history = useHistory();
if (userFromStore !== username) {
return (
<div className="alert">
<span className="closebtn" onClick={() => history.push("/console")}>×</span>
<strong>Wanning!</strong> Not authorized to access this page.
</div>
);
}
return (
<Query query={GetAuthUser}>
{({ loading, error, data }) => {
if (loading) return "loading...";
if (error) return `Error: ${error}`;
return <ProfileMutation state={state} profile={data.authUser.profile} setState={setState}/>
}}
</Query>
)
}
export default EditProfile;
ProfileMutation component
import React, { useRef, useEffect } from 'react';
import { Mutation } from 'react-apollo';
import { UpdateUserProfile } from './operations.graphql';
import "trix/dist/trix.css";
import "./styles.css"
const ProfileMutation = ({ state, profile, setState }) => {
const trixInput = useRef('');
let { userId, firstName, lastName, avatar, bio, gender, country } = profile;
let userID = parseInt(userId);
// console.log(firstName)
useEffect(() => {
trixInput.current.addEventListener('trix-change', (e) => {
setState({bio: trixInput.current.value})
console.log(trixInput.current.value)
})
},[trixInput])
return (
<Mutation mutation={UpdateUserProfile}>
{(editProfile, { loading }) => (
<div className="card bg-light col-md-8 offset-md-2 shadow p-3 mb-5 rounded">
<article className="card-body">
<form onSubmit={ e => {
e.preventDefault();
editProfile({
variables: {
userId: userID,
firstName: state.firstName,
lastName: state.lastName,
bio: state.bio,
avatar: state.avatar,
gender: state.gender,
country: state.country
}
}).then(({ data: { editProfile: { success, errors } } }) => {
success ? alert(success) : alert(errors[0]["message"]);
//TODO: replace alerts with custom message box
});
}}
>
<div className="form-row mb-3">
<div className="col">
<input
type="text"
name="firstName"
placeholder="First name"
className="form-control"
defaultValue={firstName}
onChange={e => setState({firstName: e.currentTarget.value})}
/>
</div>
<div className="col">
<input
type="text"
name="lastName"
placeholder="Last name"
className="form-control"
defaultValue={lastName}
onChange={e => setState({lastName: e.currentTarget.value})}
/>
</div>
</div>
<div className="form-group">
<label className="">Bio</label>
<input
type="hidden"
defaultValue={bio}
name="bio"
id="bio"
/>
<trix-editor input="bio" ref={trixInput} />
</div>
<input type="submit" className="btn btn-primary" value="Update Profile" disabled={loading}/>
</form>
</article>
</div>
)}
</Mutation>
);
}
export default ProfileMutation;
Hope this helps someone! If anyone has a better solution please post it here. Thanks!

Resources