首页 > 解决方案 > 当在 useEffect() 中触发 socket.on() 时,反应状态被设置回初始 []

问题描述

我是 React 功能组件的菜鸟,这是我第一次尝试使用 socket-io。

我有一个功能组件,它是一个收件箱,每页从InboxList数组中呈现多达十个“警报”行。我正在使用 socket-io,以便在单击特定行时多个客户端可以实时查看(该行变为灰色)。

问题是大多数情况下,一旦单击警报,客户端(观察行更改,而不是单击)将收件箱为空(InboxList 中的 0 个警报)。

经过一些调试,我发现一旦触发了 socket.on('alertClicked'),我的 inboxList 状态就是空的(而不是我期望的相同的 10 个警报)。

这将导致我的inboxListCopy内部greyOutAlert()当然是空的。

如果有人有建议,将不胜感激。

上下文的功能组件代码:

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

标签: javascriptreactjssocket.ioreact-hooks

解决方案


我建议您将确切的初始状态传递给 useState 挂钩,而不是像下面这样的函数:

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

推荐阅读