簡體   English   中英

React state 在 useEffect() 中觸發 socket.on() 時被設置回初始 []

[英]React state being set back to initial [] when socket.on() is triggered inside useEffect()

我是 React 功能組件的菜鳥,這是我第一次嘗試使用 socket-io。

我有一個功能組件,它是一個收件箱,每頁從InboxList數組呈現多達十個“警報”行。 我正在使用 socket-io,以便在單擊特定行時多個客戶端可以實時查看(該行變為灰色)。

問題是大多數情況下,一旦單擊警報,客戶端(觀察行更改,而不是單擊)將使其收件箱為空(InboxList 中的 0 個警報)。

經過一些調試,我發現一旦觸發了 socket.on('alertClicked'),我的 inboxList state 就為空(而不是我期望的相同的 10 個警報)。

這將導致我在inboxListCopy greyOutAlert()中的 inboxListCopy 當然為空。

如果有人有建議,將不勝感激。

上下文的功能組件代碼:

import React, { useState, useContext, useEffect } from 'react';
import io from 'socket.io-client';
import Axios from 'axios';
import Table from 'react-bootstrap/Table';
import Pagination from 'react-responsive-pagination';
import { useHistory } from 'react-router-dom';
import '../../App.css';

let socket;

export default function Inbox() {

    const [token, setToken] = useState(() => {
        return localStorage.getItem('auth-token');
    });
    const [currentPage, setCurrentPage] = useState(() => {
        return 1;
    });
    const [alertCount, setAlertCount] = useState(() => {
        return 5;
    });
    const [inboxList, setInboxList] = useState(() => {
        return [];
    });

    const history = useHistory();

    useEffect(() => {
        const connectionOptions = {
            "force new connection" : true,
            "reconnectionAttempts": "Infinity", 
            "timeout" : 10000,                  
            "transports" : ["websocket"] 
        }

        socket = io('http://localhost:5000', connectionOptions);
        
        return () => {
            socket.emit('disconnected');
            socket.off();
        }
    }, []);

    useEffect(()=>{
        fetchAlertCount();
     },[])

    useEffect(()=>{
        socket.on('alertClicked', (alertId) => {
            greyOutAlert(alertId);
          });
     },[])

    const greyOutAlert = (alertId) => {
        let inboxListCopy = [...inboxList];
        for (let i = 0; i < inboxListCopy.length; i++) {
            if (inboxListCopy[i]._id === alertId) {
                inboxListCopy[i].hasBeenReviewed = true;
                break;
            }
        }
        setInboxList(inboxListCopy);
    };

    useEffect(() => {
        fetchAlerts();
    },[currentPage]);

    const fetchAlertCount = async () => {
        const config = {
            headers: {
                'x-auth-token': token,
            }
        };
        const alertCountResponse = await Axios.get('http://localhost:5000/alertinbox/totalAlerts', config);
        setAlertCount(alertCountResponse.data.count);
    };

    const fetchAlerts = async () => {
        const config = {
            headers: {
                'x-auth-token': token,
                'page': currentPage
            }
        };
        const alertsResponse = await Axios.get('http://localhost:5000/alertinbox/', config);
        setInboxList(alertsResponse.data.alerts);
    };

    const handleClick = (alertId, messageType) => {

        socket.emit('clientClickedAlert', {alertId});

        switch (messageType) {
            case '2+ Minutes':
                history.push(`/2plusminutes/${alertId}`, {alertId});
                break;
            case 'SOS':
                history.push(`/sos/${alertId}`, {alertId});
                break;
            case 'Low Battery':
                history.push(`/lowbattery/${alertId}`, {alertId});
                break;
        };
    };

    return (
        <React.Fragment>
            <div id='inbox'>
                <Table>
                    <thead>
                        <tr>
                            <th>Alert Type</th>
                            <th>Customer Name</th>
                            <th>Timestamp</th>
                            <th>Vehicle</th>
                        </tr>
                    </thead>
                    <tbody>
                        {inboxList.map(function(alert, index){
                            return(
                                <tr key={index} onClick={() => handleClick(alert._id, alert.messageType)} className={alert.hasBeenReviewed ? 'darken' : 'lighten'}>
                                    <td>{alert.messageType}</td>
                                    <td>{alert.ownerName}</td>
                                    <td>{alert.createdAt}</td>
                                    <td>{alert.vehicle.year + ' ' + alert.vehicle.make + ' ' + alert.vehicle.model}</td>
                                </tr>
                            )
                        })}
                    </tbody>
                </Table>
            </div>
            <Pagination
                current={currentPage}
                total={Math.ceil(alertCount/10)}
                onPageChange={setCurrentPage}
                maxWidth={100}
            />
        </React.Fragment>
    )
}

我建議您將確切的初始 state 傳遞給 useState 掛鈎,而不是像下面的 function :

const [inboxList, setInboxList] = useState([]);

好吧,我想通了。 如果您需要更新 state 的東西,而新的 state 依賴於以前的 state,請確保使用功能更新

在我的代碼中,我需要當前的 inboxList state(實際上是之前的 state,因為 useEffect() 在渲染后運行)來計算新的 inboxList Z9ED39E2EA931586B6A985A6942EF57 為了實現這一點,我使用了作為 React 的 useState 鈎子一部分的可選回調。

正確的代碼(useEffect() 方法更新):

  useEffect(()=>{
    socket.on('alertClicked', (alertId) => {
        setInboxList(inboxList => {
            let inboxListCopy = [...inboxList];
            for (let i = 0; i < inboxListCopy.length; i++) {
                if (inboxListCopy[i]._id === alertId) {
                    inboxListCopy[i].hasBeenReviewed = true;
                    break;
                }
            }
            return inboxListCopy;
        });
      });
 },[])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM