<-- Whenever I try to update user info,I am not getting the updated values.But when I reload the page,I get the updated values.Is there a way to get the updated values whenever I hit the update button which is in MiddlePanel? Right now I am fetching all users in loadUsers. -->
class Home extends Component {
state = {
loadUsers: [],
currentUser: null,
};
async componentDidMount() {
const res = await axios.get("http://localhost:5000/users");
this.setState({ loadUsers: res.data });
}
setUser = (currentUser) => {
this.setState({ currentUser });
};
render() {
return (
<Fragment>
<div className="row">
<div className="col-md-3" style={{ backgroundColor: "#303F9F" }}>
<Typography variant="h6">List all Counsellors</Typography>
{this.state.loadUsers.map((user) => {
const { _id, firstname, lastname } = user;
return (
<div key={_id}>
<PrimaryButton
onClick={(e) => {
this.setUser(user);
}}
>
{firstname} {lastname}
</PrimaryButton>
</div>
);
})}
</div>
<div className="col-md-4">
{this.state.currentUser && (
<div>
<MiddlePanel user={this.state.currentUser} />
</div>
)}
</div>
</div>
</Fragment>
);
}
}
export default Home;
Here is my middlepanel code
const MiddlePanel = ({ user }) => {
const [data, setData] = useState({
firstname: "",
lastname: "",
email: "",
phoneNo: "",
});
const { firstname, lastname, email, phoneNo } = data;
useEffect(() => {
const fetchUser = async () => {
const res = await axios.get(`http://localhost:5000/users/${user._id}`);
setData({
firstname: res.data.firstname,
lastname: res.data.lastname,
email: res.data.email,
phoneNo: res.data.phoneNo,
});
};
fetchUser();
}, [user._id]);
const handleChange = (e) => {
const { name, value } = e.target;
setData({ ...data, [name]: value });
};
const handleSubmit = async (e) => {
e.preventDefault();
const newUser = { firstname, lastname, email, phoneNo };
try {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify(newUser);
await axios.patch(
`http://localhost:5000/users/${user._id}`,
body,
config
);
} catch (err) {
console.log(err);
}
};
return (
<div>
<Form onSubmit={handleSubmit}>
<Input
type="text"
name="firstname"
onChange={handleChange}
value={data.firstname}
/>
<Input
type="text"
name="lastname"
onChange={handleChange}
value={data.lastname}
/>
<Input
type="email"
name="email"
onChange={handleChange}
value={data.email}
/>
<Input
type="tel"
name="phoneNo"
onChange={handleChange}
value={data.phoneNo}
/>
<PrimaryButton>Update</PrimaryButton>
</Form>
</div>
);
};
I want to display the updated user info which I am doing in the Middlepanel
try this code , I tested this in codesandbox it works
import React, { Component, Fragment } from "react";
import axios from "axios";
class Home extends Component {
state = {
loadUsers: [],
currentUser: null
};
async componentDidMount() {
const res = await axios.get("https://reqres.in/api/users?page=2");
this.setState({ loadUsers: res.data.data });
}
setUser = (currentUser) => {
console.log(currentUser);
this.setState({ currentUser });
};
render() {
return (
<Fragment>
<div className="row">
<div className="col-md-3" style={{ backgroundColor: "#303F9F" }}>
{/* <Typography variant="h6">List all Counsellors</Typography> */}
{this.state.loadUsers
? this.state.loadUsers.map((user) => {
const { _id, first_name, lastname } = user;
return (
<div key={_id}>
<button onClick={()=> this.setUser(first_name)}>
{first_name} {lastname}
</button>
</div>
);
})
: null}
</div>
<div className="col-md-4">
{this.state.currentUser && <div>{this.state.currentUser}</div>}
</div>
</div>
</Fragment>
);
}
}
export default Home;
Related
I have very simple ediable book-list. But when I press 'edit' and edit just one of two fields the other one behaves unexpectedly (it clears out even tho there was a value, or takes unexpected value from God knows where). It works correctly only if I edit both fields at once.
Here's my code:
import './App.css';
import React, { useState, useEffect } from 'react';
function App() {
const [books, setBooks] = useState([]);
const [book, setBook] = useState({
id: '',
title: '',
author: ''
});
const [alertMessage, setAlertMessage] = useState(false);
const [successMessage, setSuccessMessage] = useState(false);
const [editMode, setEditMode] = useState(null);
const [titleValue, setTitleValue] = useState('');
const [authorValue, setAuthorValue] = useState('');
useEffect(()=> {
const data = localStorage.getItem('books');
if(data) {
setBooks(JSON.parse(data))
}
}, [])
useEffect(()=> {
localStorage.setItem('books', JSON.stringify(books))
},)
function addBook (e) {
e.preventDefault();
if(!book.title && !book.author){
setAlertMessage(true)
setTimeout(()=>setAlertMessage(false), 3000)
} else {
let newBook = {
...book,
id: Math.floor(Math.random() * 100000000),
};
setBooks([newBook, ...books]);
setBook({
title: '',
author: ''
});
setSuccessMessage(true)
setTimeout(()=>setSuccessMessage(false), 1000);
}
}
function deleteBook(id){
setBooks(books.filter(book => book.id !== id))
}
function editBook(id) {
setEditMode(id);
}
function onChange(e) {
setBook({
...book,
[e.target.name]: e.target.value
})
}
function saveChanges (id) {
let newBook = [...books].map(book => {
if(book.id === id) {
book.title = titleValue;
book.author = authorValue
}
return book
});
setBook(newBook);
setEditMode(null)
}
return (
<div className='container'>
{alertMessage && <div className='alertMeaage'>Please, enter book author or its title</div>}
{successMessage && <div className='successMessage'>Book is successfully added!</div>}
<div className='BookForm'>
<h3>Add book</h3>
<input name='title' type='text' placeholder='Enter book title' value={book.title} onChange={onChange}/>
<input name='author' type='text' placeholder='Enter book author' value={book.author} onChange={onChange}/>
<button className='submitBtn' onClick={addBook}>Send</button>
</div>
<div>
<h4>Recently added books:</h4>
<div key={book.id}>{books.map(book => (
<div className='bookItem'>
{editMode !== book.id ? <><span className='titleAuthor'>Title: </span><i>«{book.title}» </i>
<span className='titleAuthor'>Author: </span> <i>{book.author}</i>
<button onClick={()=>deleteBook(book.id)} className='deleteBtn'>X</button>
<button onClick={()=>editBook(book.id)} className='editBtn'>Edit</button></>
:
<form className='form'>
<input name='title' type='text' defaultValue={book.title} onChange={(e)=> setTitleValue(e.target.value)}/>
<input name='author' type='text' defaultValue={book.author} onChange={(e)=> setAuthorValue(e.target.value)}/>
<button className='saveBtn' onClick={()=>saveChanges(book.id)}>Save</button>
</form>
}
</div>
))}
</div>
</div>
</div>
);
}
export default App;
Thanks a lot in advance!
When you edit new book, authorValue and titleValue still have previous values, so you must setAuthorValue and setTitleValue in editBook function. See below:
function editBook(book) {
setEditMode(book.id);
setTitleValue(book.title);
setAuthorValue(book.author);
}
And handle event:
<button onClick={() => editBook(book)} className="editBtn">
Edit
</button>
All code:
// import './App.css';
import React, { useState, useEffect } from "react";
function App() {
const [books, setBooks] = useState([]);
const [book, setBook] = useState({
id: "",
title: "",
author: ""
});
const [alertMessage, setAlertMessage] = useState(false);
const [successMessage, setSuccessMessage] = useState(false);
const [editMode, setEditMode] = useState(null);
const [titleValue, setTitleValue] = useState("");
const [authorValue, setAuthorValue] = useState("");
useEffect(() => {
const data = localStorage.getItem("books");
if (data) {
setBooks(JSON.parse(data));
}
}, []);
useEffect(() => {
localStorage.setItem("books", JSON.stringify(books));
});
function addBook(e) {
e.preventDefault();
if (!book.title && !book.author) {
setAlertMessage(true);
setTimeout(() => setAlertMessage(false), 3000);
} else {
let newBook = {
...book,
id: Math.floor(Math.random() * 100000000)
};
setBooks([newBook, ...books]);
setBook({
title: "",
author: ""
});
setSuccessMessage(true);
setTimeout(() => setSuccessMessage(false), 1000);
}
}
function deleteBook(id) {
setBooks(books.filter((book) => book.id !== id));
}
function editBook(book) {
setEditMode(book.id);
setTitleValue(book.title);
setAuthorValue(book.author);
}
function onChange(e) {
console.log(e.target.name, e.target.value);
setBook({
...book,
[e.target.name]: e.target.value
});
}
function saveChanges(id) {
let newBook = [...books].map((book) => {
if (book.id === id) {
book.title = titleValue;
book.author = authorValue;
}
return book;
});
setBook(newBook);
setEditMode(null);
}
return (
<div className="container">
{alertMessage && (
<div className="alertMeaage">
Please, enter book author or its title
</div>
)}
{successMessage && (
<div className="successMessage">Book is successfully added!</div>
)}
<div className="BookForm">
<h3>Add book</h3>
<input
name="title"
type="text"
placeholder="Enter book title"
value={book.title}
onChange={onChange}
/>
<input
name="author"
type="text"
placeholder="Enter book author"
value={book.author}
onChange={onChange}
/>
<button className="submitBtn" onClick={addBook}>
Send
</button>
</div>
<div>
<h4>Recently added books:</h4>
<div key={book.id}>
{books.map((book) => (
<div className="bookItem">
{editMode !== book.id ? (
<>
<span className="titleAuthor">Title: </span>
<i>«{book.title}» </i>
<span className="titleAuthor">Author: </span>{" "}
<i>{book.author}</i>
<button
onClick={() => deleteBook(book.id)}
className="deleteBtn"
>
X
</button>
<button onClick={() => editBook(book)} className="editBtn">
Edit
</button>
</>
) : (
<form className="form">
<input
name="title"
type="text"
defaultValue={book.title}
onChange={(e) => setTitleValue(e.target.value)}
/>
<input
name="author"
type="text"
defaultValue={book.author}
onChange={(e) => setAuthorValue(e.target.value)}
/>
<button
className="saveBtn"
onClick={() => saveChanges(book.id)}
>
Save
</button>
</form>
)}
</div>
))}
</div>
</div>
</div>
);
}
export default App;
I find myself struggling to change the form input value from the button onClick handler. My problem is escalated by the fact that I have the line items on a form and things become even more complex that way. I tried to bring in a ref to set value,,, the value is displayed but at the form state after submission it remains empty which means the value is not being set. Below are all my code files. May someone kindly help me on where I may be missing the point.
class VehicleTemplate extends React.Component {
constructor(props){
super(props);
this.ws = new WebSocket('ws://localhost:8000/ws/weightData/');
this.socketRef = null;
this.state = {
data: 0,
name: '',
model: '',
plate_number: '',
type: 'truck',
wagons: [{ index: uuid(), label: '', type: 'once', weight: '' }],
total: 0,
};
this.onSubmit = this.onSubmit.bind(this);
this.handleChange= this.handleChange.bind(this);
this.handleChangeTable = this.handleChangeTable.bind(this);
this.addNewRow = this.addNewRow.bind(this);
this.deleteRow = this.deleteRow.bind(this);
this.handleLineItemChange = this.handleLineItemChange.bind(this);
}
componentDidMount() {
this.ws.onopen = () => {
// on connecting, do nothing but log it to the console
console.log('connected')
}
this.ws.onmessage = evt => {
// listen to data sent from the websocket server
// const message = JSON.parse(evt.data)
const {data} = this.state;
this.setState({data: evt.data})
// this.setState({this.state.lines.weight: data})
}
this.ws.onclose = () => {
console.log('disconnected')
// automatically try to reconnect on connection loss
}
};
onSubmit = (e) => {
e.preventDefault();
const {
name,
model,
plate_number,
type,
wagons,
} = this.state;
const vehicle = {
name,
model,
plate_number,
type,
wagons,
};
this.props.addVehicle(vehicle, this.props.token);
console.log(vehicle);
this.setState({
name: '',
model: '',
plate_number: '',
wagons: [],
});
};
handleLineItemChange = (elementIndex) => (event) => {
let wagons = this.state.wagons.map((item, i) => {
if (elementIndex !== i) return item
return {...item, [event.target.name]: event.target.value}
})
this.setState({wagons})
}
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
handleChangeTable = (name, id) => event => {
this.updateItem(id, { [name]: event.target.value });
};
addNewRow = (event) => {
this.setState({
wagons: this.state.wagons.concat(
[{index: uuid(), label: '', type: 'once', weight: '' }]
)
})
}
deleteRow = (index) => (event) => {
this.setState({
wagons: this.state.wagons.filter((item, i) => {
return index !== i
})
})
}
handleReorderLineItems = (newLineItems) => {
this.setState({
wagons: newLineItems,
})
}
clickOnDelete(record) {
this.setState({
wagons: this.state.lines.filter(r => r !== record)
});
}
render() {
const { classes } = this.props;
const {
data,
name,
model,
plate_number,
wagons,
} = this.state;
return (
<InformationTechnologyLayout>
<PapperBlock title="ADD VEHICLE" icon="ios-document-outline" desc="VEHICLE">
<Form>
<div style={{ clear: 'both' }} />
<h1>WEIGHT: {data}KG</h1>
<Grid container>
<Grid item xs={6}>
<Controls.Input
name="name"
label="Name"
value={name}
onChange={this.handleChange('name')}
/>
<Controls.Input
name="model"
label="MODEL"
value={model}
onChange={this.handleChange('model')}
/>
</Grid>
<Grid item xs={6}>
<Controls.Input
name="plate_number"
label="PLATE NUMBER"
value={plate_number}
onChange={this.handleChange('plate_number')}
/>
</Grid>
</Grid>
<Extensions
items={wagons}
addHandler={this.addNewRow}
changeHandler={this.handleLineItemChange}
focusHandler={this.handleFocusSelect}
deleteHandler={this.deleteRow}
reorderHandler={this.handleReorderLineItems}
data ={data}
/>
<div className='valueTable'>
<div className='row'>
<div className='label'>Subtotal</div>
<div className='value'>{this.calcLineItemsTotal()}KG</div>
</div>
<div className='row'>
<div className='label'>Total Due</div>
<div className='value'>{this.calcGrandTotal()}KG</div>
</div>
</div>
<Button variant="contained" onClick={this.onSubmit}>SUBMIT</Button>
</Form>
</PapperBlock>
</InformationTechnologyLayout>
);
}
}
On top is the main form component which has got Vehicle exetnsions and I am getting my weight from a websocket connection streaming scale outputs.
class Extensions extends Component {
handleDragEnd = (result) => {
if (!result.destination) return
// helper function to reorder result (src: react-beautiful-dnd docs)
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list)
const [removed] = result.splice(startIndex, 1)
result.splice(endIndex, 0, removed)
return result
}
// perform reorder
const lineItems = reorder(
this.props.items,
result.source.index,
result.destination.index
)
// call parent handler with new state representation
this.props.reorderHandler(lineItems)
}
render = () => {
const {
items,
addHandler,
changeHandler,
focusHandler,
deleteHandler,
reorderHandler,
products,
data,
readOnly,
} = this.props
return (
<form>
<div className='lineItems'>
<div className='gridTable'>
<DragDropContext onDragEnd={this.handleDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
className= 'listDraggingOver'
>
{this.props.items.map((item, i) => (
<Draggable key={item.index} draggableId={item.index} index={i}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={provided.draggableProps.style}
className='listItemDragging'
>
<Extension
addHandler={addHandler}
changeHandler={changeHandler}
focusHandler={focusHandler}
deleteHandler={deleteHandler}
reorderHandler={reorderHandler}
data={data}
style={{color: 'red'}}
key={i + item.index}
index={i}
name={item.index}
label={item.label}
weight={item.weight}
/>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div>
<div className='addItem'>
<button type="button" onClick={addHandler}><AddIcon size="1.25em" className='addIcon' /> Add Item</button>
</div>
</div>
</form>
)
}
}
This are the extensions and the last file is where I would like to capture weight from
class LineItem extends Component {
constructor(props){
super(props);
this.ref = React.createRef();
this.state = {
buttonState: false,
};
}
requestWeight = () => {
const { weight } = this.props;
const {buttonState} =this.state;
this.ref.current.value = this.props.data;
this.setState({
buttonState: true
})
};
render = () => {
const { index, label, weight } = this.props;
const {buttonState} = this.state;
return (
<div className='lineItem'>
<div>{index + 1}</div>
<Controls.Input
name="label"
label="LABEL"
value={label}
onChange={this.props.changeHandler(index)}
/>
<input
className='currency'
type='number'
readOnly={true}
ref={this.ref}
name='weight'
onChange={this.props.changeHandler(index)}
/>
<div>
<button type="button"
className='requestItems'
onClick={this.requestWeight}
disabled={buttonState}
>REQUEST WEIGHT</button>
</div>
<div>
<button type="button"
className='deleteItem'
onClick={this.props.deleteHandler(index)}
><DeleteIcon size="1.25em" /></button>
</div>
</div>
)
}
}
export default LineItem
If I add the value and put weight on the input then the ref stops working. I assume I might be missing something between my handleChange function and the requestWeight function but I really dont know what exaclty
I'm trying to require the name and email in my form. I originally had <button> nested within a <Link> and figured out that the <button> worked by itself to allow the input to be required, but that when it's nested within the <Link> it doesn't require it. I then added the link to be within the submitSurvey function and took the <Link> away from the render. It still won't require the input.
Here is my NameEmailComponent.js:
import React, { useState } from "react";
import { NameInput } from "../inputs/NameInput";
import { EmailInput } from "../inputs/EmailInput";
import { useHistory } from "react-router-dom";
export const NameEmailComponent = (props) => {
const [surveyValues, setSurveyValues] = useState({});
const [inlineData, setInlineData] = useState({});
const [question, setQuestion] = useState({});
const history = useHistory();
console.log(props);
const triggerBackendUpdate = () => {
setSurveyValues({});
setQuestion({});
};
const handleSubmit = (event) => {
event.preventDefault();
event.persist();
setSurveyValues(surveyValues);
setQuestion(question);
triggerBackendUpdate();
};
const callback = (name, value) => {
console.log("Form Data: ", name, ": ", value);
inlineData[name] = value;
setInlineData(inlineData);
console.log(inlineData);
};
const handleChange = (event) => {
event.preventDefault();
this.setState({ value: event.target.value });
console.log("Name: ", event.target.value);
};
const submitSurvey = async () => {
try {
await fetch("/api/survey", {
method: "POST",
body: JSON.stringify(inlineData),
headers: {
"Content-Type": "application/json",
},
});
history.push({ pathname: "/survey" });
} catch (err) {
console.log(err);
}
};
const inputs = props.inputs
? props.inputs.filter((inputOption) => inputOption)
: [];
return (
<>
<div id="nameContainer" className="form-group">
<form onSubmit={handleSubmit}>
{inputs.map((data, index) => {
let inputKey = `input-${index}`;
return data.type === "text" ? (
<NameInput
className="form-control my-3"
triggerCallback={callback}
name={data.name}
type={data.type}
placeholder={data.placeholder}
required={true}
onChange={handleChange}
key={inputKey}
/>
) : (
<EmailInput
className="form-control mt-3"
triggerCallback={callback}
name={data.name}
type={data.type}
placeholder={data.placeholder}
required={true}
onChange={handleChange}
key={inputKey}
/>
);
})}
<div className="col-6 mx-auto text-center">
<div className="button">
<button
className="btn btn-primary mt-4 mb-2 mx-5"
type="submit"
onClick={submitSurvey}
>
Begin Survey
</button>
</div>
</div>
</form>
</div>
</>
);
};
Here is my NameInput.js:
import React from "react";
import { useInputChange } from "../Hooks/useInputChangeHook";
import { isTextInput } from "../validators";
export const NameInput = (props) => {
const inputType = isTextInput(props.type) ? props.type : "text";
const { handleChange } = useInputChange(
props.defaultValue,
props.triggerCallback,
inputType
);
const inputProps = {
className: props.className ? props.className : "form-control",
onChange: handleChange,
required: props.required,
question: props.question,
placeholder: props.placeholder,
type: inputType,
options: props.options,
name: props.name ? props.name : `${inputType}_${props.key}`,
};
return (
<>
<div id={props.name}>
<input
{...inputProps}
required={props.required}
value={props.value || ""}
/>
</div>
</>
);
};
I am new to react and designing an eCommerce website. In the admin side of the website, I have created a database to store customers information. I am able to write to the database and read from it (data is appearing in the state) but I am not able to display the data on screen. I have a function called 'displayCustomerList' to get the data from the state and format it but in the Render part, nothing is being displayed. Any help or advice is much appreciated.
import React, { Component } from "react";
import Title from "./Title";
import { Link, Redirect } from "react-router-dom";
import { ButtonContainer } from "./Button";
import axios from "axios";
export default class Admin extends Component {
constructor(props) {
super(props);
const token = localStorage.getItem("token");
let loggedIn = true;
if (token == null) {
loggedIn = false;
}
this.state = {
loggedIn,
name: "",
address: "",
postcode: "",
phone: "",
posts: [],
};
}
componentDidMount = () => {
this.getCustomerList();
};
getCustomerList = () => {
axios
.get("/api")
.then((response) => {
const data = response.data;
this.setState({ posts: data });
console.log("Data has been received");
})
.catch(() => {
alert("Error retrieving data");
});
};
handleChange = ({ target }) => {
const { name, value } = target;
this.setState({ [name]: value });
};
submit = (event) => {
event.preventDefault();
const payload = {
name: this.state.name,
address: this.state.address,
postcode: this.state.postcode,
phone: this.state.phone,
};
axios({
url: "/api/save",
method: "POST",
data: payload,
})
.then(() => {
console.log("Data has been sent to the server");
this.resetUserInputs();
this.getCustomerList();
})
.catch(() => {
console.log("Internal server error");
});
};
resetUserInputs = () => {
this.setState({
name: "",
address: "",
postcode: "",
phone: "",
});
};
//THIS IS CAUSING THE ISSUE
displayCustomerList = (posts) => {
if (!posts.length) return null;
console.log(posts) // I can see the array in the console
posts.map((post, index) => (
<div key={index} className="customer.list_display">
<h3>TEST</h3> //NOT DISPLAYING
<h3>{post.name}</h3>
<p>{post._id}</p>
<p>{post.address}</p>
<p>{post.postcode}</p>
<p>{post.phoneNumber}</p>
</div>
));
};
render() {
console.log("State: ", this.state);
if (this.state.loggedIn === false) {
return <Redirect to="/login" />;
}
return (
<React.Fragment>
<div className="py-5">
<div className="container">
<Title name="Admin" />
<Link to="/">Logout</Link>
</div>
</div>
<div className="card-footer d-flex justify-content-between">
<form onSubmit={this.submit} className="py-5">
<div className="form-input">
<input
type="text"
name="name"
placeholder="Name"
value={this.state.name}
onChange={this.handleChange}
className="nameInput"
/>
</div>
<div className="form-input">
<input
type="address"
name="address"
placeholder="Address"
value={this.state.address}
onChange={this.handleChange}
className="addressInput"
/>
</div>
<div className="form-input">
<input
type="text"
name="postcode"
placeholder="Postcode"
value={this.state.postcode}
onChange={this.handleChange}
className="postcodeInput"
/>
</div>
<div className="form-input">
<input
type="text"
name="phone"
placeholder="Phone number"
value={this.state.phone}
onChange={this.handleChange}
className="phoneInput"
/>
</div>
<ButtonContainer>submit</ButtonContainer>
</form>
</div>
<div>
{/* <CustomerList /> */}
<Title name="Customer List" />
</div>
//NOTHING IS BEING DISPLAYED
<div className="blog-">
{this.displayCustomerList(this.state.posts)}
</div>
</React.Fragment>
);
}
}
you should return JSX object from displayCustomerList like this:
displayCustomerList = (posts) => {
if (!posts.length) return null;
console.log(posts) // I can see the array in the console
return posts.map((post, index) => (
<div key={index} className="customer.list_display">
<h3>TEST</h3> //NOT DISPLAYING
<h3>{post.name}</h3>
<p>{post._id}</p>
<p>{post.address}</p>
<p>{post.postcode}</p>
<p>{post.phoneNumber}</p>
</div>
));
};
I have a removeUser page where I am using a < Mutation > and then I am doing my error handling using the submitForm() function. This code worked perfectly well:
export default function RemoveUserPage() {
const [isSubmitted, setIsSubmitted] = useState(false);
const [isRemoved ,setIsRemoved] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
function StatusMessage(){
if (isRemoved){
return (
<CustomAlert severity='success' text='User Removed'></CustomAlert>
)
}
//else...
}
function submitForm(RemoveUserMutation: any, email: string) {
setIsSubmitted(true);
RemoveUserMutation({
variables: {
email: email,
},
}).then(({ data }: any) => {
setIsRemoved(true);
})
.catch((error: { message: string; }) => {
setIsRemoved(false);
console.log("Error msg:" + error.message);
setErrorMessage(error.message)
})
}
return (
<Mutation mutation={RemoveUserMutation}
>
{(RemoveUserMutation: any) => (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<Formik
initialValues={{ email: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
};
return (
<div className='main-content'>
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(RemoveUserMutation, email)}}>
<div>
<TextField
variant="outlined"
margin="normal"
id="email"
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<br></br>
<Button
type="submit"
disabled={!isValid || !email}
>
Remove User</Button>
</div>
</form>
<br></br>
{isSubmitted && StatusMessage()}
</div>
)
}}
</Formik>
</div>
)}
</Mutation>
);
}
However, I was suggested to use useMutationinstead. Firstly, I am unable to do so since I get such errors:
Unhandled Rejection (Error): GraphQL error: Variable `email` of type `String!` must not be null.
And even if the mutation works, is there any way I can still modify and use the same function for error handling in my case?
This is what I was trying now but this doesn't work:
export default function RemoveUserPage() {
const [isSubmitted, setIsSubmitted] = useState(false);
const [isRemoved ,setIsRemoved] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [removeUser] = useMutation(RemoveUserMutation);
function StatusMessage(){
if (isRemoved){
return (
<CustomAlert severity='success' text='User Removed'></CustomAlert>
)
}
}
function submitForm(RemoveUserMutation: any, email: string) {
setIsSubmitted(true);
RemoveUserMutation({
variables: {
email: email,
},
}).then(({ data }: any) => {
setIsRemoved(true);
})
.catch((error: { message: string; }) => {
setIsRemoved(false);
setErrorMessage(error.message)
})
}
return (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<Formik
initialValues={{ email: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
};
return (
<div className='main-content'>
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
removeUser({variables: {todo: email }});}}>
<div>
<TextField
variant="outlined"
margin="normal"
id="email"
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<br></br>
<Button
type="submit"
disabled={!isValid || !email}
>
Remove User</Button>
</div>
</form>
<br></br>
{isSubmitted && StatusMessage()}
</div>
)
}}
</Formik>
</div>
);
}
There's no reason to have a RemoveUserMutation anymore -- removeUser is already in the scope, so just use it.
function submitForm(email: string) {
setIsSubmitted(true);
removeUser({
variables: {
email,
},
})
...
}
You can continue to use your submitForm function like this:
onSubmit={e => {
e.preventDefault();
submitForm(email);
}}
This line
removeUser({variables: {todo: email }})
isn't working because there is no todo variable. Since your using TypeScript, you should generate type definitions for your queries and then use them with the hooks. This would prevent mistakes like this.