Context: I have a server component that fetches a list of organizations and passes it on to a client component. The client component fills in the organizations
state from the prop data. Later on, it does operations on it making changes to the data within. One example is changing the state of an organization from active to archived.
ServeComponent
export default async function ServerComponent({searchParams}) {
const pageNumber = !!searchParams.page ? searchParams.page : 1;
const organizations = await getOrganizations(session?.user.email, pageNumber);
return <Organizations initialOrganizations={organizations}/>
}
ClientComponent
export default function Organizations({ initialOrganizations = [] }) {
const [organizations, setOrganizations] = useState(initialOrganizations);
const [selectedOrganization, setSelectedOrganization] = useState({});
const [toggle, setToggle] = useState(true);
const [tabs, setTabs] = useState([{ name: 'Active', current: true },
{ name: 'Archive', current: false }]);
const [activeTab, setActiveTab] = useState('Active');
const router = useRouter();
const search = useSearchParams();
useEffect(() => {
const page = search.get('page');
if(page) {
router.push(`/portal/organizations?page=${page}`)
}
}, [search])
const handleOrganizationUpdate = (updatedOrganization) => {
setOrganizations((prevOrganizations) =>
prevOrganizations.map((o) =>
o.id === updatedOrganization.id ? updatedOrganization : o
)
);
};
const archiveOrganization = async (organizationId) => {
const response = await fetch('/api/organization/archive',{
method: 'POST',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organizationId })
});
if(response.ok) {
const responseBody = await response.json();
const updatedOrganization = responseBody.response;
console.log(updatedOrganization)
setOrganizations((prev) =>
prev.map((organization) =>
organization.id === updatedOrganization.id ? { ...organization, status: updatedOrganization.status } : organization
)
);
}
}
return (
toggle ?
<>
<Button variant='solid' href='/portal/organizations/create-organization'>
<span>Create Organization</span>
</Button>
<Tab tabs={tabs} setTabs={setTabs} setActiveTab={setActiveTab}/>
{organizations.map((organization) => (
activeTab.toUpperCase() == organization.status &&
<div
key={organization.id}
className="group flex justify-between mt-10 flex-row items-center hover:bg-slate-900 w-3/5 px-8 py-6 mb-5 border-b-cyan-50 rounded shadow-black hover:shadow-black shadow-md hover:shadow-lg"
>
<div className='flex flex-col'>
<h2 className='text-xs text-slate-300'>Organization Name</h2>
<h2 onClick={() => {setToggle(false); setSelectedOrganization(organization)}} className="text-base hover:underline">{organization.name}</h2>
</div>
<Button onClick={() => {archiveOrganization(organization.id)}} className="opacity-0 transition-opacity duration-100 ease-in-out group-hover:opacity-100" variant="solid">
{organization.status == "ACTIVE" ? <span>Archive</span> : <span>Active</span>}
</Button>
</div>
))}
</>
:
<Organization organization={selectedOrganization} handleOrganizationUpdate={handleOrganizationUpdate} setToggle={setToggle}/>
);
}
Problem: When the component is mounted and I do some operation, it shows the updated array. On remount it uses the stale data which was initially passed as props, which is obvious given that the props don't change and the state is re-initialized.
Please suggest a pattern which can solve this problem, which perhaps could be:
- Re-fetch the data on the server whenever I navigate to the endpoint /organizations.
- Alternative logic in the client component to not use the stale data.