So I have a table of salespeople that is populated using the map method. Each row in that table has an 'Edit' button that should 1) get the id of the row, 2) fetch the data for that record from the database using react query, and 3) open a modal and pass that the object data to the modal to populate the input fields.
At this point, I can do the above by passing the data to the modal directly using the react query response, but I want to put that response into a useState
variable to track my state locally for client operations (e.g. when adding a new salesperson, I need to pass an empty object into the modal instead of the last query result).
I'm pretty sure to do this, I need to incorporate useEffect
somehow, but I'm having trouble getting it to work properly. Would think it would be as easy as updating a usestate variable in the useeffect function, like so:
I tried doing something like this:
const [salespersonToModify, setSalespersonToModify] = React.useState({})
.....
React.useEffect(() => {
console.log(`effect salesperson:${idToEdit}`)
setSalespersonToModify(fetchedSalesperson)
}, [fetchedSalesperson])
.....
<ModifySalespersons
isOpen={isOpen}
setIsOpen={setIsOpen}
salesperson={salespersonToModify}
onSubmit={handleSubmit}
/>
But I'm not having any luck. Hoping someone can assist. Full code below
import React, { useState, useEffect, useRef } from 'react';
import { useQuery, useMutation, useQueryCache, QueryCache, ReactQueryCacheProvider } from 'react-query'
import ModifySalespersons from '../components/ModifySalespersons'
function Salespersons(props){
const [page, setPage] = React.useState(1)
const [hasNextPage, setHasNextPage] = React.useState(true)
const [hasPreviousPage, setHasPreviousPage] = React.useState(false)
const [isOpen, setIsOpen] = React.useState(false)
const [idToEdit, setIdToEdit] = React.useState(0)
// api calls to abstract out
const fetchSalespersons = async (key, { page = 1 }) => {
const res = await fetch(`http://localhost:5000/api/salespersons?pagenumber=${page}&pagesize=4`);
let pagination = JSON.parse(res.headers.get("X-Pagination"));
setHasNextPage(pagination.hasNext);
setHasPreviousPage(pagination.hasPrevious);
return res.json();
}
const fetchSalesperson = async (key, salespersonId) => {
const res = await fetch(`http://localhost:5000/api/salespersons/${salespersonId}`);
return res.json();
}
const { data: salespersons, status: salespersonsStatus } = useQuery(['salespersons', { page } ], fetchSalespersons)
const { data: fetchedSalesperson, status: fetchedSalespersonStatus } = useQuery(['foundSalesperson', idToEdit], fetchSalesperson)
function addSalesPerson(){
// setFetchedSalesperson({});
setIsOpen(true);
}
function editSalesPerson(salespersonId){
fetchSalesperson(salespersonId)
.then((salesperson) => {
setIdToEdit(salespersonId)
setIsOpen(true);
});
}
React.useEffect(() => {
console.log(`effect salesperson:${idToEdit}`)
}, [idToEdit])
function handleSubmit(newSalesperson) {
console.log(newSalesperson);
// call api to create or update record
setIsOpen(false);
}
return (
<>
{fetchedSalespersonStatus === 'success' && (
<ModifySalespersons
isOpen={isOpen}
setIsOpen={setIsOpen}
salesperson={fetchedSalesperson}
onSubmit={handleSubmit}
/>
)}
{salespersonsStatus === 'loading' && (
<div class="fixed inset-0 transition-opacity flex items-center justify-center">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
<button type="button" class="z-50 inline-flex items-center px-12 py-8 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150 cursor-not-allowed" disabled="">
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<p className="text-xl">
Loading
</p>
</button>
</div>
)}
{salespersonsStatus === 'error' && (
<div>
Error
</div>
)}
{salespersonsStatus === 'success' && (
<div onKeyDown={(event) => {if(event.key === "Escape") setIsOpen(false)}} tabIndex="0">
<div className="pb-5 border-b border-gray-200 space-y-3 sm:flex sm:items-center sm:justify-between sm:space-x-4 sm:space-y-0">
<h2 className="text-lg leading-6 font-medium text-gray-900">
Salespeople
</h2>
<div>
<span className="shadow-sm rounded-md">
<button onClick={ () => addSalesPerson() } type="button" className="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:shadow-outline-indigo focus:border-indigo-700 active:bg-indigo-700 transition duration-150 ease-in-out">
Add new salesperson
</button>
</span>
</div>
</div>
<div className="flex flex-col">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
Id
</th>
<th className="px-6 py-3 bg-gray-50"></th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{ salespersons.map((salesperson, index) => (
<tr>
<td className="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500">
{salesperson.salespersonId}
</td>
<td className="px-6 py-4 whitespace-no-wrap text-right text-sm leading-5 font-medium">
<button onClick={ () => editSalesPerson(salesperson.salespersonId) } className="text-indigo-600 hover:text-indigo-900">Edit</button>
</td>
</tr>
))}
</tbody>
</table>
<nav className="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
<div className="flex-1 flex justify-between sm:justify-end">
<PageButtons page={page} setPage={setPage} hasPreviousPage={hasPreviousPage} hasNextPage={hasNextPage}></PageButtons>
</div>
</nav>
</div>
</div>
</div>
</div>
</div>
)}
</>
);
}
function PageButtons(props) {
return (
<>
{ props.hasPreviousPage && (
<button onClick={ () => props.setPage(props.page - 1) } className="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
Previous
</button>
)}
{ props.hasNextPage && (
<button onClick={ () => props.setPage(props.page + 1) } className="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
Next
</button>
)}
</>
);
}
export default Salespersons;
The algorithm can be like:
Practically, your editSalesPerson could be
be sure that your comp Salespersons wrapped by:
all work out of Modal has done. then you have to extract needed parameter from location.
inside your modal component(or just here) instead of your
const [idToEdit, setIdToEdit] = React.useState(0)
you already have an edit id which is
last part is to implement fetching inside your modal