socket.on 在地图 React 中发送 socket.emit() 时状态未更改

socket.on State Not Changing while sending socket.emit() in a map React

我正在使用 socket.ioreact 进行项目。这是我的组件

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { io } from 'socket.io-client';
import Button from '../../components/Button';
import { IProject } from '../../interfaces/projects';
import { IRun } from '../../interfaces/runs';

const socket = io(process.env.REACT_APP_SERVER_URL);

export default function RunAll() {
    const { search } = useLocation();

    // API State
    const [project, setProject] = useState<IProject | undefined>(undefined);
    const [runs, setRuns] = useState<IRun[]>([]);

    // Query Params
    const queryParams = new URLSearchParams(search);
    const projectId = queryParams.get('projectId')!;

    // Get Project
    useEffect(() => {
        (async () => {
            const { data: project } = await axios.get(`${process.env.REACT_APP_SERVER_URL}/api/projects/${projectId}`);

            setProject(project.data);
        })();
    }, [projectId]);

    // Clear socket
    useEffect(() => () => {
        socket.close();
    });

    const runAllTests = async () => {
        project?.tests.forEach((test) => {
            console.log(test);
            socket.emit('create run', { projectId, testId: test.id, url: process.env.REACT_APP_SERVER_URL });
        });

        socket.on('created run', (run: IRun) => {
            console.log(run);
            setRuns([...runs, run]);
        });
    };
    console.log(runs);

    const renderHeader = () => (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', alignItems: 'center' }} className='mb-3'>
            <h1 className='heading-primary mt-auto mb-auto'>Run Overview</h1>

            <Button onClick={runAllTests}>Run All Tests</Button>
        </div>
    );

    return (
        <main>
            {renderHeader()}
            {runs?.map((run) => (
                <div>{run.id}</div>
            ))}
        </main>
    );
}

单击按钮并调用 runAllTests() 时,我可以在控制台中看到 console.log(test),我的服务器日志也显示它已收到 socket.emit('create run')。当服务器响应 socket.on('created run') 时,我可以看到创建的 运行 的值。但是,只有第二个 运行(如果 project.tests 的长度为 2),只有最后一个 运行 被添加到状态。

我在这里错过了什么?请帮帮我!提前致谢!

    socket.on('created run', (run: IRun) => { console.log(run); setRuns([...runs, run]); }); 

在 useEffect 中使用此代码并确保它只运行一次。以下应该适合您。

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { io } from 'socket.io-client';
import Button from '../../components/Button';
import { IProject } from '../../interfaces/projects';
import { IRun } from '../../interfaces/runs';

const socket = io(process.env.REACT_APP_SERVER_URL);

export default function RunAll() {
    const { search } = useLocation();

    // API State
    const [project, setProject] = useState<IProject | undefined>(undefined);
    const [runs, setRuns] = useState<IRun[]>([]);

    // Query Params
    const queryParams = new URLSearchParams(search);
    const projectId = queryParams.get('projectId')!;

    // Get Project
    useEffect(() => {
        (async () => {
            const { data: project } = await axios.get(`${process.env.REACT_APP_SERVER_URL}/api/projects/${projectId}`);

            setProject(project.data);
        })();
    }, [projectId]);

    // Clear socket
    useEffect(() => () => {
        socket.on('created run', (run: IRun) => {
            console.log(run);
            setRuns([...runs, run]);
        });
        
        return function cleanup () {
           socket.close();
        }

    },[]);

    const runAllTests = async () => {
        project?.tests.forEach((test) => {
            console.log(test);
            socket.emit('create run', { projectId, testId: test.id, url: process.env.REACT_APP_SERVER_URL });
        });

        
    };
    console.log(runs);

    const renderHeader = () => (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', alignItems: 'center' }} className='mb-3'>
            <h1 className='heading-primary mt-auto mb-auto'>Run Overview</h1>

            <Button onClick={runAllTests}>Run All Tests</Button>
        </div>
    );

    return (
        <main>
            {renderHeader()}
            {runs?.map((run) => (
                <div>{run.id}</div>
            ))}
        </main>
    );
}