组件不会在状态更改时重新渲染

时间:2021-04-15 16:06:42

标签: reactjs react-hooks state rendering render

我的问题是当我的状态改变时我的组件没有重新渲染。我在自定义 Hook 中管理我的状态,在向我的后端发出放置请求后,我的状态得到更新。这完全正常,但是在提出请求后更改状态时,我的页面内容不会刷新。

组件:

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;

自定义钩子:

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)];
}

1 个答案:

答案 0 :(得分:2)

这可能是因为您的 buildPutAndFetch 函数正在改变状态中的 values 数组,而不是创建新的引用。如果引用没有改变,React 将 bail out on state updates

当您声明 arr 变量时,它会将 arr 设置为与 values 相同的引用,而不是创建一个新实例。您可以使用扩展运算符创建副本:const arr = [...values]

还值得注意的是,由于这是异步发生的,您可能需要使用 setValuesfunction updater 形式来确保在执行更新时拥有最新的一组值。

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;
});