I currently have a simple next.js website where users can look at projects for an organization, and at the bottom of the page, the user can input a new project through the use of a form with multiple inputs. The database that i am currently using is Supabase.
My code currently takes in user input from each input box and stores them inside the newProject const, after which the data is then parsed into the createNewProject function and sent to Supabase.
const initialState = { solution_id: '', organization_id: '', budget_usd: '',other_info: '',country: '',project_duration_days: '',status: '',date_time_timezone: '' }
export default function Projects({ Projects }) {
useEffect(() => {
console.log(Projects)
}, [])
const [newProject, setProject] = useState(initialState)
console.log("User inputed data")
console.log(newProject)
const {solution_id, organization_id, budget_usd, other_info, country,project_duration_days,status,date_time_timezone} = newProject
const router = useRouter()
function onChange(e) {
setProject(() => ({ ...newProject, [e.target.name]: e.target.value }))
}
async function createNewProject() {
if (!solution_id || !organization_id || !country) return
const id = uuid()
newProject.id = id
const {data} = await supabase
.from('Projects')
.insert([
{ solution_id, organization_id, budget_usd,other_info,country,project_duration_days,status,date_time_timezone }
])
router.push(`/projects/${data.id}`)
}
return (
<div>
{Projects.map(project => {
return (
<div key={project.id}>
<h1><b>{project.Solutions.name} in {project.country}</b></h1>
<h2>Budget USD: {project.budget_usd}</h2>
<h2>Duration: {project.project_duration_days} days</h2>
<h2>Status: {project.status}</h2>
<h2>Organization: {project.Organizations.name}</h2>
<h2>Project Id: {project.id}</h2>
<button type="button" onClick={() => router.push(`/projects/${project.id}`)}>Donate now</button>
<br></br>
</div>
)
})}
<label htmlFor="solution_id ">solution_id</label>
<input onChange={onChange} value={newProject.solution_id} type="text" id="solution_id" name="solution_id" required />
<label htmlFor="organization_id ">organization_id</label>
<input onChange={onChange} value={newProject.organization_id} type="text" id="organization_id" name="organization_id" required />
<label htmlFor="budget_usd ">Last budget_usd</label>
<input onChange={onChange} value={newProject.budget_usd} type="text" id="budget_usd" name="budget_usd" required />
<label htmlFor="other_info ">other_info</label>
<input onChange={onChange} value={newProject.other_info} type="text" id="other_info" name="other_info" required />
<label htmlFor="country ">country</label>
<input onChange={onChange} value={newProject.country} type="text" id="country" name="country" required />
<label htmlFor="project_duration_days ">Project Duration Days</label>
<input onChange={onChange} value={newProject.project_duration_days} type="text" id="project_duration_days" name="project_duration_days" required />
<label htmlFor="status ">status</label>
<input onChange={onChange} value={newProject.status} type="text" id="status" name="status" required />
<label htmlFor="date_time_timezone ">date_time_timezone</label>
<input onChange={onChange} value={newProject.date_time_timezone} type="text" id="date_time_timezone" name="date_time_timezone" required />
<button type="button" onClick={createNewProject} >Submit new project</button>
</div>
)
}
export async function getServerSideProps() {
const fetchOrgs = async () => {
let { data: Organizations, error } = await supabase
.from('Organizations')
.select('*')
return Organizations
}
const fetchSolutions = async () => {
let { data: Solutions, error } = await supabase
.from('Solutions')
.select('*')
return Solutions
}
const fetchProjects = async () => {
let { data: Projects, error } = await supabase
.from('Projects')
.select(`
id,
solution_id,
organization_id,
budget_usd,
country,
project_duration_days,
status,
Solutions(name),
Organizations(name)
`)
.order('id', { ascending: true })
console.log(Projects)
return Projects
}
const Organizations = await fetchOrgs();
const Solutions = await fetchSolutions();
const Projects = await fetchProjects();
return { props: { Solutions, Organizations, Projects } }
}
However, whenever I press the submit button, the console.log for the newProject, would show that there is not data being passed into the variables, only the empty placeholder data in the initialState const. As such, I am unsure about how to pass data from next.js input forms into a variable to be posted into supabase.
Related
I have a form, and I have an input in the form, I upload several images in this input, like so:
<input type="file" name="gallery" onChange={imageHandler} required multiple />
And when I output the images in the console, it correctly displays an array of files, but when I append the images, formData.append("gallery", images); it is not saved as an array. It's saved as a File!
const [selectedFile, setSelectedFile] = useState();
const [images, setImages] = useState([]);
const token = localStorage.getItem("TOKEN");
const imageHandler = (e) => {
setImages([...images, e.target.files[0]]);
};
useEffect(() => {
images.length && console.log(images);
}, [images]);
console.log(images);
const sendDisease = async (e) => {
e.preventDefault();
console.log(images);
const form = document.getElementById("signUpForm");
const formData = new FormData(form);
formData.append("gallery", images);
console.log(formData);
try {
const api = "https://localhost/api/v1/website/disease/add";
const { data } = await axios.post(api, formData, {
headers: {
headers: { "Content-Type": "multipart/form-data" },
Authorization: `Bearer ${token}`,
},
});
toast.success(data.message, { autoClose: 15000 });
} catch (e) {
console.log(e);
toast.error(e.response.data.error, { autoClose: 15000 });
console.log(e.response.data.error);
}
};
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0]);
};
return (
<>
<div className="container">
<div className="row">
<form id="signUpForm" className="md-12">
<h2 className="text-center mb-4 font-weight-bold">Add Disease</h2>
<div className="form-row mt-4">
<div className="col-md-6 mb-3">
<label for="">Add Disease</label>
<input
type="text"
className="form-control"
name="unique_name"
required
onChange={(e) => setUniqueName(e.target.value)}
/>
</div>
<div className="col-md-6 mb-3">
<label for="">Title</label>
<input
type="text"
className="form-control"
name="title"
placeholder="Title"
onChange={(e) => setTitle(e.target.value)}
required
/>
</div>
</div>
<div className="form-row">
<div className="col-md-12 mb-3">
<label for="">Gallery</label>
<input type="file" name="gallery" onChange={imageHandler} required multiple />
</div>
</div>
<input type="file" name="image" onChange={handleFileSelect} />
</div>
<button
type="button"
className="btn btn-primary mt-4"
onClick={sendDisease}
>
Submit
</button>
</form>
</div>
</div>
</>
);
Calling new FormData(form) is enough. You don't need to call formData.append("gallery", images) as the constructor will pick on its own all the images for you. So you may not even need this images state.
The code below would log all your files if you wanna be sure about it. I also created a working Codesandbox for testing.
export default function App() {
const handleSubmit = (e) => {
e.preventDefault();
const form = document.getElementById("signUpForm");
const formData = new FormData(form);
console.log(formData.getAll("gallery"));
};
return (
<form id="signUpForm" onSubmit={handleSubmit}>
<input type="file" name="gallery" required multiple />
<button>Sumbit</button>
</form>
);
}
If it's not working, try changing formData.append("gallery", images) to the code below (it's apparently what's needed for PHP servers for example):
images.forEach((image) => {
formData.append('gallery[]', image);
});
Here in my case Am using ASP.NET Core and its working fine
images.forEach((image) => {
formData.append('images', image);
});
My Controller
[HttpPost]
public async Task<IActionResult> UpdateProfile([FromForm] UpdateProfileModel updateProfileModel)
{
try
{
await Task.CompletedTask;
return Ok("Done.");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
My Modal
public class UpdateProfileModel
{
public List<IFormFile> Images { get; set; } = new List<IFormFile>();
}
I want to refresh my page after save of newRider to Firebase. But when I use window.location.reload(); or with (false) it does not save. Without it it works.
And is it ok to have code that long in one file?
import React from "react";
import { RidersDB } from "../../Backend/DataBase/RidersDB";
const ridersDB = new RidersDB();
export default function CrewMemberSetCreate() {
const [newIsShown, setNewIsShown] = React.useState(false);
const [newRider, setNewRider] = React.useState({
firstName: "",
lastName: "",
age: 0,
favTrick: "",
dreamTrick: "",
youtube: "",
instagram: "",
isShown: newIsShown,
img: "",
});
function handleChange(event) {
setNewRider((prevNewRider) => {
return {
...prevNewRider,
[event.target.name]: event.target.value,
};
});
}
const createRider = (event) => {
event.preventDefault();
ridersDB.createRider({
firstName: newRider.firstName,
lastName: newRider.lastName,
age: Number(newRider.age),
favTrick: newRider.favTrick,
dreamTrick: newRider.dreamTrick,
youtube: newRider.youtube,
instagram: newRider.instagram,
isShown: newIsShown,
img: "",
});
//here i want reload and tried to usewindow.location.reload(false);
};
return (
<div className="setCreate">
<h2>Add Rider</h2>
<div className="centerForm">
<form>
<input
type="text"
placeholder="First Name"
onChange={handleChange}
value={newRider.firstName}
name="firstName"
/>
<input
type="text"
placeholder="Last Name"
onChange={handleChange}
value={newRider.lastName}
name="lastName"
/>
<input
type="number"
placeholder="Age (number)"
min="1"
onChange={handleChange}
value={newRider.age}
name="age"
/>
<input
type="text"
placeholder="Favourite Trick"
onChange={handleChange}
value={newRider.favTrick}
name="favTrick"
/>
<input
type="text"
placeholder="Dream Trick"
onChange={handleChange}
value={newRider.dreamTrick}
name="dreamTrick"
/>
<input
type="text"
placeholder="Youtube Link"
onChange={handleChange}
value={newRider.youtube}
name="youtube"
/>
<input
type="text"
placeholder="Instagram Link"
onChange={handleChange}
value={newRider.instagram}
name="instagram"
/>
<div className="checkboxDiv">
<label>Show on home page?</label>
<input
type="checkbox"
onClick={() => {
setNewIsShown((prevState) => !prevState);
}}
/>
</div>
<button onClick={createRider}>Add Rider</button>
</form>
</div>
</div>
);
}
RidersDB.js
import { db } from "./firebase-config";
import { addDoc, collection, getDocs } from "firebase/firestore";
export class RidersDB {
constructor() {
this.ridersCollRef = collection(db, "ridersCrew");
this.createRider = async (riderData) => {
await addDoc(this.ridersCollRef, riderData);
};
this.getRiders = async () => {
const data = await getDocs(this.ridersCollRef);
return data.docs.map((doc) => ({ ...doc.data(), id: doc.id }));
};
}
}
The createRider() methods returns a Promise. You should wait for it to resolve and then proceed. Try refactoring the code as shown below:
const createRider = (event) => {
event.preventDefault();
return ridersDB.createRider({
firstName: newRider.firstName,
// ....
}).then(() => {
// resolved, proceed now
window.location.reload(false)
}).catch((e) => console.log(e));
};
I'm building a form using React & Nodejs & MongoDB, where you can fill the form and upload a file with some other text inputs.
However, after i submit, i receive only text inputs in my database. The chosen file (desired to upload) doesn't appear at all in the database. I am expecting to get all the data form (the uploaded file and text inputed) in my database.
I tested my backend and it works correctly (it uploads all the inputs).
Ps: In browser, when i select a file (.pdf) to upload, the file input always shows no file chosen !
Console.dev : Error
{title: 'Miss', fname: 'zaezae', lname: 'zaeazee', email: 'zaea#mail.com', phoneNumber: '12345678', …}
coverLetter: "zaez e az ezae e zae aeae "
cv: ""
email: "zaea#mail.com"
fname: "zaezae"
lname: "zaeazee"
myFile: "C:\\fakepath\\test.pdf"
phoneNumber: "12345678"
title: "Miss"
Formulaire.jsx:29
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'then')
at submit (Formulaire.jsx:29:1)
Backend:
server.js:
const express = require('express')
const mongoose = require('mongoose')
const bodyparser = require('body-parser')
const FormRoute = require('./routes/FormRoute')
//database
mongoose.connect('mongodb://localhost:27017/form', { useNewUrlParser: true, useUnifiedTopology: true })
const db = mongoose.connection
db.on('error', (err) => {
console.log(err)
})
db.once('open', () => {
console.log("Database connection established!")
})
//app
const app = express()
app.use(bodyparser.urlencoded({ extended: true }))
app.use(bodyparser.json())
//cors
const cors = require('cors')
app.use(cors())
//server run
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
})
app.use('/api/form', FormRoute);
Axios.js:
import axios from 'axios'
export const Axios = axios.create({
baseURL: 'http://localhost:5000',
})
Apiroutes.js:
const form = "/api/form"
export const requests = {
formApi: {
store: form + '/store'
}
}
formservices.js:
import { Axios } from "../config/axios";
import { requests } from "../config/apiroutes";
export const FormService = {
store: (data) => {
Axios.post(requests.formApi.store, data)
.then(res => {
return res
})
.catch(err => {
return err
})
}
}
formController.js :
const form = require('../models/FormModel')
const store = (req, res, next) => {
let candidate = new form({
title: req.body.title,
fname: req.body.fname,
lname: req.body.lname,
email: req.body.email,
phoneNumber: req.body.phoneNumber,
coverLetter: req.body.coverLetter
})
if (req.file) {
candidate.cv = req.file.path
}
candidate.save()
.then(response => {
res.json({
success: true,
message: 'Candidate added successfully!',
data: candidate,
})
})
.catch(error => {
res.json({
success: false,
message: 'An error occured!',
error: error,
})
})
}
module.exports = {
store
}
Frontend:
Formulaire.jsx :
import React, { useState } from 'react'
import Divider from '#mui/material/Divider';
import './Formulaire.css'
import { titles } from '../../mock/titles'
import { FormService } from '../../services/formServices';
const Form = () => {
const [storedata, setstoredata] = useState({
title: '',
fname: '',
lname: '',
email: '',
phoneNumber: '',
cv: '',
coverLetter: ''
})
const handleChange = e => {
const { name, value } = e.target;
setstoredata(prevState => ({
...prevState,
[name]: value
}));
};
const submit = async () => {
console.log(storedata);
await FormService.store(storedata)
.then(res => {
console.log(res);
})
.catch(err => {
return err
})
}
return (
<div className='container'>
<div className='header'>
<div className='title'>
<a className='quizbutton' href="/quiz">Take a Test (Quiz)</a>
<h1>Apply for a Position :</h1>
</div>
</div>
<Divider style={{ maxWidth: '1000px', marginLeft: '250px' }} />
<div id="content">
<div id="formWrapper">
<form id="msform" method='post' action='/uploadFile' enctype="multipart/form-data">
<fieldset id="fieldset3">
<h2 class="fs-title">Please complete the form below for a position with us.</h2>
<h3 class="fs-subtitle">Reference 0001</h3>
{/* <div class="fs-error"></div> */}
<div class="wrapper">
<label for="title">Title :</label>
<select name="title" value={storedata.title} onChange={handleChange}>
<option hidden></option>
{
titles.map((c, i) => {
return (
<option key={i} value={c}>{c}</option>
)
})
}
</select>
<label for="fname">First Name<span>*</span> :</label>
<input type="text" name="fname" value={storedata.fname} onChange={handleChange} id="fname" placeholder="Please enter your first name" required />
<label for="lname">Last Name<span>*</span> :</label>
<input type="text" name="lname" value={storedata.lname} onChange={handleChange} id="lname" placeholder="Please enter your last name" required />
<label for="email">Email<span>*</span> :</label>
<input type="email" name="email" value={storedata.email} onChange={handleChange} id="email" placeholder="Please enter your email" required />
<label for="phoneNumber">Phone N° :</label>
<input type="number" name="phoneNumber" value={storedata.phoneNumber} onChange={handleChange} id="phoneNumber" placeholder="Phone number" />
<label for="CV">Upload CV <span>*</span>:</label>
<input type="file" name="myFile" id="cv" value={storedata.cv} onChange={handleChange} accept="application/msword, application/pdf" placeholder="Cover Letter" required />
<label for="coverLetter">Cover Letter :</label>
<textarea type="text" name="coverLetter" value={storedata.coverLetter} onChange={handleChange} id="coverLetter" placeholder="cover Letter" />
</div>
<br />
<input type="submit" name="submit" class="submit action-button" value="Submit" onClick={submit} />
</fieldset>
</form>
</div>
</div>
</div>
)
}
export default Form
I fixed my problem by changing name="myFile" id="cv" to name="cv" id="cv"
Self taught coder. Hopefully I can explain this adequately. Users create recipe cooking guides by inputing values like dish name, food category, cover img, etc. The steps array only has 1 step at the moment. I want a button that users can click that would add another "step" and "gif" to the steps array. Can anyone suggest a method to have another set of "step" and "img" render on screen when the user clicks the Add Step button? I have a handleAddSteps function with nothing inside. Somebody recommended using DOM? Just want to know if this is a good way to go before investing time down the wrong rabbit hole. Thank you kindly for any advice.
import React from "react";
import { useState, useEffect } from "react";
// Styles
import "./UploadGuide.scss";
// Firebase
import { doc, setDoc } from "firebase/firestore";
import { db, storage } from "../../firebase";
import { v4 as uuidv4 } from "uuid";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
function UploadGuide() {
const [file, setFile] = useState("");
const [data, setData] = useState({});
const [percentage, setPercentage] = useState(null);
// const [stepsCounter, setStepsCounter] = useState(0);
// Upload IMG
useEffect(() => {
const uploadFile = () => {
const name = new Date().getTime() + file.name;
const storageRef = ref(storage, file.name);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
setPercentage(progress);
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
default:
break;
}
},
(error) => {
console.log(error);
},
() => {
// Handle successful uploads on complete
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setData((prev) => ({ ...prev, img: downloadURL }));
});
}
);
};
file && uploadFile();
}, [file]);
console.log(file);
console.log(data);
// When user clicks Add Steps button, page renders another steps card
const handleAddSteps = () => {};
// When user submits guide, upload data to firecloud db and imgs to firebase storage.
const handleAddGuide = async (e) => {
e.preventDefault();
const newId = uuidv4();
// const stepsArray = []
await setDoc(doc(db, "guides", newId), {
author: e.target.author.value,
categoryId: "100",
categoryName: e.target.categoryName.value,
coverGif: data.img,
id: newId,
name: e.target.name.value,
tags: e.target.tags.value,
// steps: stepsArray
steps: [
{
step: e.target.step.value,
gif: data.img,
},
],
});
};
return (
<div className="upload">
<form onSubmit={handleAddGuide} className="upload__form">
<div className="upload__card">
<div className="upload__inputs">
<label className="upload__label">Guide Name</label>
<input
className="upload__input"
placeholder="Name of your recipe"
type="name"
name="name"
// value=""
></input>
</div>
<div className="upload__inputs">
<label className="upload__label">Author</label>
<input
className="upload__input"
type="author"
name="author"
></input>
</div>
<div className="upload__inputs">
<label className="upload__label">Category</label>
<select
name="categoryName"
className="upload__input"
defaultValue={"Cooking"}
>
<option value="Cooking">Cooking</option>
<option></option>
</select>
</div>
<div className="upload__inputs">
<label className="upload__label">Tags</label>
<input
name="tags"
className="upload__input upload__input--large"
placeholder="Add relevant tags to help people find your guide"
></input>
</div>
</div>
<div className="upload__card upload__card--addstep">
<div className="upload__inputs">
<label className="upload__label">Step 1</label>
<input
className="upload__input upload__input--large"
name="step"
></input>
</div>
<div className="upload__inputs">
<div>
<img src={file ? URL.createObjectURL(file) : ""}></img>
</div>
<label
htmlFor="file"
style={{ cursor: "pointer" }}
className="upload__add"
>
+ IMG
</label>
<input
id="file"
onChange={(e) => setFile(e.target.files[0])}
style={{ display: "none" }}
type="file"
></input>
</div>
</div>
<div className="upload__box">
<button
className="upload__add"
onClick={handleAddSteps}
style={{ cursor: "pointer" }}
>
Add Step
</button>
</div>
<div className="upload__cta">
<button
disabled={percentage !== null && percentage < 100}
type="submit"
className="upload__submit"
style={{ cursor: "pointer" }}
>
Upload
</button>
</div>
</form>
</div>
);
}
export default UploadGuide;
Here is an image of what the page looks like:
enter image description here
Please let me know if I'm missing any key information. Any advice is appreciated. Thank you.
Since each Guide can have multiple steps, I would rather create a state that handles an array of multiple step objects, than just a counter.
With this, you can render as much steps as you have in the array.
Then, adding steps just will be a matter of adding objects (with an empty state) to that array.
I made this functional example, that should be clear enough to show what I mean:
import React, { useState } from 'react';
const EMPTY_STEP = { text: '', file: null };
export default function App() {
const [guideSteps, setGuideSteps] = useState([{ ...EMPTY_STEP }]);
const handleStepChange = (stepIndex, property, value) => {
setGuideSteps((prevState) => {
const newGuideSteps = [...prevState];
newGuideSteps[stepIndex] = { ...newGuideSteps[stepIndex], [property]: value };
return newGuideSteps;
});
};
const handleAddStep = () => {
setGuideSteps((prevState) => [...prevState, { ...EMPTY_STEP }]);
};
return (
<div className="Steps">
{guideSteps.map((step, index) => {
return (
<div key={`step-${index + 1}`}>
<h2>{`Step ${index + 1}`}</h2>
<input type="text" value={step.text} onChange={(e) => handleStepChange(index, 'text', e.target.files ? e.target.files[0] : null, index)} />
<input type="file" value={step.file} onChange={(e) => handleStepChange(index, 'file', e.target.value, index)} />
</div>
);
})}
<button onClick={handleAddStep}>Add Step</button>
</div>
);
}
I recently switched my reactjs code to nextjs code, and I've observed that while I'm in reactjs code, When I delete data or perform a delete action, it appears like the queries are re-fetched and I am given the most recent or updated data in the datatable, but when I attempt it on Nextjs, it does not work. Is there a way to fix this?
Keep note I am using client side for this action.
Code
Form.js
export default function MainCategoryForm() {
const [name, setName] = useState("");
const [file, setFile] = useState();
const [status, setStatus] = useState("");
const [createMainCategory, { loading }] = useMutation(
CREATE_MAINCATEGORY_MUTATION
);
async function onSubmit() {
const res = await createMainCategory({
variables: {
name,
slug: name.toLowerCase(),
file,
status,
},
update: (cache, { data: { createMainCategory } }) => {
const { mainCategories } = cache.readQuery({
query: FETCH_MAINCATEGORIES_QUERY,
});
cache.writeQuery({
query: FETCH_MAINCATEGORIES_QUERY,
data: { mainCategories: mainCategories.concat([createMainCategory]) },
});
},
refetchQueries: [{ query: FETCH_MAINCATEGORIES_QUERY }],
});
if (res) {
toast.success(`Main Category Created`, { autoClose: 2000 });
setName("");
setStatus("");
setFile("");
}
}
console.log(file);
return (
<>
<Form onSubmit={onSubmit} className={loading ? "loading" : ""}>
<h2>Create a Main Category:</h2>
<Form.Field>
<input
name="file"
type="file"
onChange={(event) => {
setFile(event.target.files[0]);
}}
/>
<Form.Input
placeholder="Please Enter Name"
name="name"
label="Name: "
onChange={(event) => {
setName(event.target.value);
}}
value={name}
/>
<label>Status: </label>
<select
name="category"
className="form-control"
onChange={(event) => {
setStatus(event.target.value);
}}
value={status}
>
<option active="true" hidden>
Please Enter Status
</option>
<option value="Activated">Activated</option>
</select>
<br />
<Button type="submit" color="teal">
Submit
</Button>
</Form.Field>
</Form>
</>
);
}
As shown above this code I have this refetchQueries: [{ query: FETCH_MAINCATEGORIES_QUERY }], in which after the add mutation or mutation it will refetch the query needed for the recent data to show in my datatable, I tried also putting that in the DeleteButton Component but it doesn't work.
Table
export default function MainCategoryTable({
mainCategory: { id, name, slug, status, url, createdAt },
}) {
return (
<>
<tr>
<td>{id}</td>
<td>
<img src={url} width={300} />
</td>
<td>{name}</td>
<td>{slug}</td>
<td>{status}</td>
<td>{dayjs(createdAt).format("h:mm:ss a")}</td>
<td>
<DeleteButton name={name} mainCategoryId={id} />
<Button>
<Link href={`/mainCategories/${id}`}>
<Icon name="edit" style={{ margin: 0 }} />
</Link>
</Button>
</td>
</tr>
</>
);
}
DeleteButton Component
export default function DeleteButton({ mainCategoryId, callback }) {
const [confirmOpen, setConfirmOpen] = useState(false);
const mutation = DELETE_MAINCATEGORY_MUTATION;
const [deleteMainCategoryOrMutation] = useMutation(mutation, {
update(proxy) {
setConfirmOpen(false);
if (mainCategoryId) {
const data = proxy.readQuery({
query: FETCH_MAINCATEGORIES_QUERY,
});
data.getMainCategories = data.getMainCategories.filter(
(ap) => ap.id !== mainCategoryId
);
toast.error(`Main Category Deleted`, { autoClose: 2000 });
proxy.writeQuery({ query: FETCH_MAINCATEGORIES_QUERY, data });
}
if (callback) callback();
},
variables: {
mainCategoryId,
},
});
return (
<>
<MyPopup content={"Delete Main Category"}>
<Button
as="div"
color="red"
floated="right"
onClick={() => setConfirmOpen(true)}
>
<Icon name="trash" style={{ margin: 0 }} />
</Button>
</MyPopup>
<Confirm
open={confirmOpen}
onCancel={() => setConfirmOpen(false)}
onConfirm={deleteMainCategoryOrMutation}
/>
</>
);
}
If you need any more code, such as my backend or any files to figure out what's wrong, I'll always amend my article. If you need any clarification or don't understand what I mean, please leave a comment down below.
You didn't set the refetchQueries in the DELETE_MAINCATEGORY_MUTATION mutation, instead you used the update option and read the query from the cache but you mutated the data, which is not the right way to do it, instead you should return a new array as follows:
const [deleteMainCategoryOrMutation] = useMutation(mutation, {
update(proxy) {
setConfirmOpen(false);
if (mainCategoryId) {
const previousData = proxy.readQuery({ query: FETCH_MAINCATEGORIES_QUERY });
const getMainCategories = previousData.getMainCategories.filter(
(ap) => ap.id !== mainCategoryId
);
const data = {
getMainCategories,
};
toast.error(`Main Category Deleted`, { autoClose: 2000 });
proxy.writeQuery({ query: FETCH_MAINCATEGORIES_QUERY, data });
}
if (callback) callback();
},
variables: {
mainCategoryId,
},
});