简体   繁体   中英

Error with websocket connection when trying to add dependencies

I am currently trying to add charts for the graphical part with React in an Electron software. Except that I added interactions with buttons (sections) to insert different data in the graphs depending on the click on one of the sections by the user (variable selectedSection). So I added in the dependencies of the useEffect() function the chartPMS and chartPFS functions to have access at the selectedSection variable.

The useEffect() function receives data continuously through a websocket from a python program. The problem is that when I run the code via the npm start command, I get a data display with a very high frequency and this error continuously in the console: WebSocket connection to 'ws:<URL>/' failed: WebSocket is closed before the connection is established. But the functions did receive changes to the selectedSection variable based on clicks on the different sections.

I should point out that I used the useEffect() function in this way before, it worked but I didn't have access to the updated version after clicking on one of the sections of the selectedSection variable:

  useEffect(() => {
    const socket = new WebSocket('ws://localhost:8000');

    socket.addEventListener('message', (event) => {
      setData(JSON.parse(event.data));

      chartPFS(JSON.parse(event.data));
      chartPMS(JSON.parse(event.data));
    });

  }, []);

I added selectedSection to the dependencies except that it refreshes both panels after clicking on one of the section buttons.

Here are the code:

App.js with 2 panels :

import React, { useState, useEffect, useRef, useSyncExternalStore } from 'react';
import Modal from './Modal/Modal'
import {Chart as ChartJS,LinearScale,PointElement,LineElement,Tooltip,Legend,Title,CategoryScale,elements} from 'chart.js';
import {Scatter, Line } from 'react-chartjs-2';
import { handleDataClick } from './Modal/Modal';
import { LineChart } from 'recharts';
import 'chart.js/auto';

ChartJS.register(
    CategoryScale,
    LinearScale,
    PointElement,
    LineElement,
    Tooltip,
    Legend,
    Title);

//--------------------------- OPTIONS GRAPHIQUE ----------------------------------//

  export const options5 = {
    elements: {
      line: {
          tension: 0.3,
      },
    },
    responsive: true,
    maintainAspectRatio:false,
    plugins: {
      showLine:true,
      legend: false
    },
  };

//--------------------------- FUNCTION APP() ----------------------------------//
export default function App() {
  let da;
  const [data, setData] = useState(null);
  const [show,setShow] = useState(false);
  const [lastSelectedSection, setLastSelectedSection] = useState(null);
  const h2f5Ref = useRef(null);
  const h2f4Ref = useRef(null);
  const h2f3Ref = useRef(null);
  const h2f2Ref = useRef(null);
  const h2f1Ref = useRef(null);

  const h2m5Ref = useRef(null);
  const h2m4Ref = useRef(null);
  const h2m3Ref = useRef(null);
  const h2m2Ref = useRef(null);
  const h2m1Ref = useRef(null);

  const [selectedDataType, setSelectedDataType] = useState({id:"fs-sec-1",selected:"twist"});
  const [sectionData, setSectionData] = useState({
    "fs-sec-1": { selectedDataType: 'twist' },
    "fs-sec-2": { selectedDataType: 'twist' },
    "fs-sec-3": { selectedDataType: 'twist' },
    "fs-sec-4": { selectedDataType: 'twist' },
    "fs-sec-5": { selectedDataType: 'twist' },
    "ms-sec-1": { selectedDataType: 'twist' },
    "ms-sec-2": { selectedDataType: 'twist' },
    "ms-sec-3": { selectedDataType: 'twist' },
    "ms-sec-4": { selectedDataType: 'twist' },
    "ms-sec-5": { selectedDataType: 'twist' }
  });

  const [selectedSection, setSelectedSection] = useState("s1");
  const [selectedSailP3,setSelectedSailP3]=useState("fs");

  //----------------------- Graphiques Variables initiales  -------------------//


    const [chartDataPFS,setChartDataPFS]=useState({
      datasets: [
          {
              label: 'Draft',
              showLine:true,
              data: [{x:3,y:1},{x:3.5,y:2},{x:5.5,y:3},{x:5.25,y:4},{x:5,y:5}],
              backgroundColor: '#df9305',
              borderColor: '#df9305'
          }]
    });
    const [chartDataPMS,setChartDataPMS]=useState({
      labels:["0","1","2","3","4"],
      datasets: [
          {
              label: 'Draft',
              showLine:true,
              data: [0,2,3,2,0],
              backgroundColor: '#df9305',
              borderColor: '#df9305'
          }]
    });
 
    //----------------------- Graphiques Fonctions mise à jour  -------------------//
  const chartPFS=(d) =>{
    let dataToUse;
    console.log(selectedSection)
    dataToUse=[{x:0,y:0},
      {x:3.3/2,y:d["fs"][selectedSection]["camber"]*0.75},
      {x:3.3,y:d["fs"][selectedSection]["draft"]},
      {x:(10-3.3)/2+3.3,y:d["fs"][selectedSection]["draft"]*0.55},
      {x:10,y:0}];
    setChartDataPFS({
        datasets: [
            {
              label: 'Profile',
              showLine:true,
              maintainAspectRatio:false,
              fill:false,
              data: dataToUse,
              backgroundColor: '#000000',
              borderColor: '#000000'
            }]
    });
  };
  const chartPMS=(d) =>{
    let dataToUse;
    dataToUse=[0,
      d["ms"][selectedSection]["camber"],
      d["ms"][selectedSection]["draft"],
      d["ms"][selectedSection]["draft"],
      0];
    setChartDataPMS({
        labels:[0,1,2,3,4],
        datasets: [
            {
              label: 'Profile',
              maintainAspectRatio:false,
              fill:false,
              data: dataToUse,
              borderColor: '#000000'
            }]
    });
  };

  //----------------------- Fonctions Récupération données au clic  -------------------//

  const handleClick = (id,h2Text) => {
    const sectionId = id;
    setSelectedDataType({id:sectionId,selected:h2Text});
  };
  const handleSectionClick=(section) =>{
    setSelectedSection(section);
  };
  const handleSailP3Click=(sail) =>{
    setSelectedSailP3(sail);
  };

    //----------------------- Mise à jour données  -------------------//
    useEffect(() => {
      const socket = new WebSocket('ws://localhost:8000');

      const handler = (event) => {

        setData(JSON.parse(event.data));
        chart1(JSON.parse(event.data));
        chart2(JSON.parse(event.data));
        chart3(JSON.parse(event.data));
        chart4(JSON.parse(event.data));
        chartPFS(JSON.parse(event.data));
        chartPMS(JSON.parse(event.data));
      };

      socket.addEventListener('message', handler);

      return () => {
        socket.removeEventListener('message', handler);
        socket.close();
      };
    }, [selectedSection]);
  
  
  return (
        <div>
            <div className="home">
                <div className="template-1" id="temp1">
                <div className="panel-1">
                    <div className="panel-header">
                    <h1>FORESAIL data</h1>
                    <i className='bx bx-cog modal-trigger-panel'></i>
                    </div>
                    <div className="panel-body">
                    <div className="sec-5 modal-trigger-data" id="fs-sec-5" onClick={()=>{handleClick("fs-sec-5",h2f5Ref.current.textContent);setShow(true);}} >
                        {data && sectionData["fs-sec-5"].selectedDataType ? <span class="h1" id="h1-fs-s5">{data["fs"]["s5"][sectionData["fs-sec-5"].selectedDataType]}</span> : <span class="h1" id="h1-fs-s5">--</span>}
                        <h2 ref={h2f5Ref}>{sectionData["fs-sec-5"].selectedDataType ? sectionData["fs-sec-5"].selectedDataType.toUpperCase() : "TWIST"}</h2>
                        <h3>s5</h3>
                    </div>
                    <div className="sec-4 modal-trigger-data" id="fs-sec-4" onClick={()=>{handleClick("fs-sec-4",h2f4Ref.current.textContent);setShow(true);}}>
                        {data && sectionData["fs-sec-4"].selectedDataType ? <span class="h1" id="h1-fs-s4">{data["fs"]["s4"][sectionData["fs-sec-4"].selectedDataType]}</span> : <span class="h1" id="h1-fs-s4">--</span>}
                        <h2 ref={h2f4Ref}>{sectionData["fs-sec-4"].selectedDataType ? sectionData["fs-sec-4"].selectedDataType.toUpperCase() : "TWIST"}</h2>
                        <h3>s4</h3>
                    </div>
                    <div className="sec-3 modal-trigger-data" id="fs-sec-3" onClick={()=>{handleClick("fs-sec-3",h2f3Ref.current.textContent);setShow(true);}}>
                        {data && sectionData["fs-sec-3"].selectedDataType ? <span class="h1" id="h1-fs-s3">{data["fs"]["s3"][sectionData["fs-sec-3"].selectedDataType]}</span> : <span class="h1" id="h1-fs-s3">--</span>}
                        <h2 ref={h2f3Ref}>{sectionData["fs-sec-3"].selectedDataType ? sectionData["fs-sec-3"].selectedDataType.toUpperCase() : "TWIST"}</h2>
                        <h3>s3</h3>
                    </div>
                    <div className="sec-2 modal-trigger-data" id="fs-sec-2" onClick={()=>{handleClick("fs-sec-2",h2f2Ref.current.textContent);setShow(true);}}>
                        {data && sectionData["fs-sec-2"].selectedDataType ? <span class="h1" id="h1-fs-s2">{data["fs"]["s2"][sectionData["fs-sec-2"].selectedDataType]}</span> : <span class="h1" id="h1-fs-s2">--</span>}
                        <h2 ref={h2f2Ref}>{sectionData["fs-sec-2"].selectedDataType ? sectionData["fs-sec-2"].selectedDataType.toUpperCase() : "TWIST"}</h2>
                        <h3>s2</h3>
                    </div>
                    <div className="sec-1 modal-trigger-data" id="fs-sec-1" onClick={()=>{handleClick("fs-sec-1",h2f1Ref.current.textContent);setShow(true);}}>
                        {data && sectionData["fs-sec-1"].selectedDataType ? <span class="h1" id="h1-fs-s1">{data["fs"]["s1"][sectionData["fs-sec-1"].selectedDataType]}</span> : <span class="h1" id="h1-fs-s1">--</span>}
                        <h2 ref={h2f1Ref}>{sectionData["fs-sec-1"].selectedDataType ? sectionData["fs-sec-1"].selectedDataType.toUpperCase() : "TWIST"}</h2>
                        <h3>s1</h3>
                    </div>
                    </div>
                </div>
                <div class="panel-5">
                    <div class="panel-header">
                        <h1>SAILS sections</h1>
                        <i class='bx bx-cog modal-trigger-panel'></i>
                    </div>
                    <div class="panel-body">
                        <div class="profil-container" style={{display: "flex", flexDirection: "row"}}>
                            <div style={{width: "50%",height: "100%"}}>
                                <Scatter options={options5} data={chartDataPFS}/>
                            </div>
                            <div style={{width: "50%",height: "100%"}}>
                                <Line options={options5} data={chartDataPMS}/>
                            </div>
                        </div>
                        <div class="button-sec">
                            <input type="submit" value="Section 5" class={`section5 ${selectedSection === "s5" ? 'active' : ''}`} onClick={()=>{handleSectionClick("s5")}}/>
                            <input type="submit" value="Section 4" class={`section4 ${selectedSection === "s4" ? 'active' : ''}`} onClick={()=>{handleSectionClick("s4")}}/>
                            <input type="submit" value="Section 3" class={`section3 ${selectedSection === "s3" ? 'active' : ''}`} onClick={()=>{handleSectionClick("s3")}}/>
                            <input type="submit" value="Section 2" class={`section2 ${selectedSection === "s2" ? 'active' : ''}`} onClick={()=>{handleSectionClick("s2")}}/>
                            <input type="submit" value="Section 1" class={`section1 ${selectedSection === "s1" ? 'active' : ''}`} onClick={()=>{handleSectionClick("s1")}}/>
                        </div>
                        <div class="button-sail">
                            <input type="submit" value="Foresail" class="btn-hs modal-trigger-hs"/>
                            <input type="submit" value="Mainsail" class="btn-ms modal-trigger-ms"/>                            
                        </div>
                    </div>
                </div>
                </div>
                <Modal onClose={() => setShow(false)} show={show} Data={selectedDataType} sectionData={sectionData} setSectionData={setSectionData}/>
            </div>
        </div> 
  );
}

Python :

import asyncio
import random
import datetime
import websockets
import json

sv={"fs":{
    "s5":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s4":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s3":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s2":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s1":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    },
    "ms":{
    "s5":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s4":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s3":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s2":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},
    "s1":{"entry":2,"cfwd":3,"camber":2,"draft":3,"caft":5,"exit":5,"twist":15,"saglat":10,"saglong":10},    
    }}

async def handler(websocket, path):
    while True:
        #log_decoder()
        for key1 in sv:
            for key2 in sv[key1]:
                sv[key1][key2]["entry"] = random.randint(1, 10)
                sv[key1][key2]["cfwd"] = random.randint(1, 10)
                sv[key1][key2]["camber"] = random.randint(1, 10)
                sv[key1][key2]["draft"] = random.randint(1, 4)
                sv[key1][key2]["caft"] = random.randint(1, 10)
                sv[key1][key2]["exit"] = random.randint(1, 10)
                sv[key1][key2]["twist"] = random.randint(1, 10)
                sv[key1][key2]["saglat"] = random.randint(1, 10)
                sv[key1][key2]["saglong"] = random.randint(1, 10)        
        #data = [random.randint(0, 20) for _ in range(10)]
        await websocket.send(json.dumps(sv))
        await asyncio.sleep(1)

start_server = websockets.serve(handler, "localhost", 8000)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Regards,

If you set the dependency to be selectedSection it should solve your problem. This is because that when you set the dependency to the functions it will rerender basically as fast as your computer allows, but if you set it to selectedSection it only rerender when that is updated and the correct value is included.

Briefly scanning over your code, it seems as if the problem may be caused by the way your useEffect() method handles the WebSocket connection. When dealing with websockets, the error message "WebSocket connection failed: WebSocket being closed before the connection is established" typically refers to a websocket that is being closed before it has a chance to establish a connection.

One other thing that might be causing this is that you're initiating a new WebSocket connection each time the component re-renders, which could also be the culprit. I've encountered this issue a few times in one of my projects; however, putting the WebSocket connection outside of useEffect() so that it's only instantiated once fixed the issue.

One last thing I would try is using useEffect() with an empty array as the dependencies if you only intend to run the connection once on mount. So when you want to update selectedSection , you can use setSelectedSection to update the state, and then use that state value in useEffect() to determine how to handle the data coming in over the websocket.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM