My problem is that my component doesnt rerender, when my state changes. I am managing my state in a custom Hook and after an put request to my backend my state gets updated. This works completely fine, but the content of my page doesnt get refreshed when changing my sate after the put request.
Component:
import React, { useEffect, useState } from 'react';
import { CONTROLLERS, useBackend } from '../../hooks/useBackend';
import Loading from '../Alerts/loading';
import {Table} from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import DropdownForm from '../Forms/dropdown';
function AdminPanel() {
const headers = ['ID', 'Title', 'Release Date', 'Producer', 'Director', 'Status', 'UTC Time', '#', '#'];
const [error, setError] = useState(false);
const [loaded, setLoaded] = useState(false);
const [requests, backend] = useBackend(error, setError);
useEffect(() => {
backend(CONTROLLERS.REQUESTS.getRequestsAdmin());
}, [])
useEffect(() => {
setLoaded(requests !== undefined);
console.log(requests);
}, [requests])
const handleUpdate = (e, result) => {
backend(CONTROLLERS.REQUESTS.put({requestStatus: result, accessToken: localStorage.accessToken}, e));
}
if(!loaded) return <Loading/>
if(error) return <p>No Access</p>
return(
<>
<DropdownForm items={['A-Z', 'Z-A', 'None']} title={'Filter'} first={2} setHandler={setFilter}/>
<DropdownForm items={['+20', '+50', 'All']} title={'Count'} first={0} setHandler={setCount}/>
{/* <DropdownForm/> */}
<Table bordered hover responsive="md">
<thead>
<tr>
{headers.map((item, index) => {
return( <th className="text-center" key={index}>{item}</th> );
})}
</tr>
</thead>
<tbody>
{requests.map((item, index) =>{
return(
<tr>
<td>{index + 1}</td>
<td>{item.movie.movieTitle}</td>
<td>{item.movie.movieReleaseDate}</td>
<td>{item.movie.movieProducer}</td>
<td>{item.movie.movieDirector}</td>
<td>{(item.requestStatus === 1 ? 'Success' : item.requestStatus ===2 ? 'Pending' : 'Denied')}</td>
<td className="col-md-3">{item.requestDate}</td>
{/* <td><span onClick={() => handleDelete(item.requestID)}><i className="fas fa-times"></i></span></td> */}
<td><span onClick={() => handleUpdate(item.requestID, 3)}><i className="fas fa-times"></i></span></td>
<td><span onClick={() => handleUpdate(item.requestID, 1)}><i className="fas fa-check"></i></span></td>
</tr>);
})}
</tbody>
</Table>
</>
);
}
// }
export default AdminPanel;
customHook:
import axios from "axios";
import { useEffect, useRef, useState } from "react";
import notify from "../Components/Alerts/toasts";
const BASE_URL = 'https://localhost:44372/api/';
const R = 'Requests/'; const M = 'Movies/'; const U = 'Users/';
const buildParams = (url, type, header, param) => {
return {url: url, type: type, header: header, param: param};
}
export const CONTROLLERS = {
REQUESTS: {
getRequestsAdmin: () => buildParams(`${R}GetRequestsAdmin`, 'post', true, {accessToken:
}
export const useBackend = (error, setError) => {
const [values, setValues] = useState([]);
async function selectFunction(objc) {
switch(objc.type) {
case 'put': return buildPutAndFetch(objc.url, objc.param, objc.header);break;
default: console.log("Error in Switch");
}
}
async function buildPutAndFetch(url, param, header) {
const finalurl = `${BASE_URL}${url}`;
return axios.put(finalurl, param, {headers: {
'Authorization': `Bearer ${(localStorage.accessToken)}`
}})
.then(res => {
if(res.data && 'accessToken' in res.data) localStorage.accessToken = res.data.accessToken;
else {
//When an object gets updated, the backend returns the updated object and replaces the old one with the //new one.
const arr = values;
const found = values.findIndex(e => e[(Object.keys(res.data))[0]] == res.data.requestID);
arr[found] = res.data;
setValues(arr);
}
setError(false);
return true;
})
.catch(err => {
setError(true);
return false;
})
}
}
function response(res) {
setValues(res.data)
setError(false);
}
return [values,
async (objc) => selectFunction(objc)];
}
It's likely due to the fact that your buildPutAndFetch
function is mutating the values
array in state, rather than creating a new reference. React will bail out on state updates if the reference doesn't change.
When you declare your arr
variable, it's setting arr
equal to the same reference as values
, rather than creating a new instance. You can use the spread operator to create a copy: const arr = [...values]
.
It's also worth noting that because this is happening asynchronously, you may want to use the function updater form of setValues
to ensure you have the most current set of values when performing the update.
setValues(prev => {
const arr = [...prev];
const found = prev.findIndex((e) => e[Object.keys(res.data)[0]] == res.data.requestID);
arr[found] = res.data;
return arr;
});