Home > Back-end >  Pass state data to ipc function call
Pass state data to ipc function call

Time:12-28

Problem

The event to send data to renderer is triggered on a file change with chokidar. When the file watcher is triggered and the process sends the event to the renderer.

My problem is that when the EmitterEvent is triggered I input the current state of my useState() variable but only the initial state is passed to my function.

Edited: my problem is that I can not pass the updated data variable to the updateData(newData) function which is called from the emitter inside the preload.js.

Question

How can I pass the state variable data to the call ?

Is there a way that I can change my preload.js in order for the api.receive function to return a string in order not to have to pass a function to the emitter ? (please check the updateData(newData) function for more info)

Is there a better way to achieve this ?

This could also help me to initialize the data for the first render.

preload.js

contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            // whitelist channels
            let validChannels = ["file", "save"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["file", "save"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        },
        

    }

electron.js

function ReadNodesFileToIpc(path) {
  fs.readFile(path, (error, data) => {
    win.webContents.send("file", data);
  });
}

Code in component that receives data

 function MyForceGraphComponent(){ 
  const [data, setData] = useState({ nodes: [{ id: 0, otherinfo: [] }], links: [] });
  var isDataInittiallized = 0;
  ...
 
  function updateData (newData, data) {
    if (data.nodes.length !== 1){ 
      // do stuff

      setData({ nodes:  data.nodes, links: data.links });
    }else{
      if (!isDataInittiallized){
        setData({ nodes:  newData.nodes, links: newData.links });
        isDataInittiallized = 1;
      }
    }
  }
  ...
  useEffect(() => {  
    ...
    window.api.receive("file", (bytesArray) => {
           var newData = JSON.parse(String.fromCharCode.apply(null, bytesArray));
           updateData(newData); // Check function bellow
              
         });
    ...
  }, []);
}

updateData(newData) (a function inside my components fuction)

isDataInittiallized is a variable inside the component that the change got passed to the emitter

data is my variable from the useState() function that the change did NOT got passed to the emitter even though the setData() previously changed the data successfully. So the length remains 1 and it contains the same elements from when it was firstly initialized .

Other info

Have tried to play with passing the data variable to the receive function without any success.

Most probably when the emitter is getting set the function passed (the one that does JSON.parse) is getting passed along and never changed after.

CodePudding user response:

Since the issue is related to stale data inside the updateData function, I suggest to make the following updates:

// Simplified for the sake of brevity

function MyForceGraphComponent() {
  const [data, setData] = useState({ nodes: [{ id: 0 }] })

  // isDataInitialized needs to be tracked just as any other state
  const [isDataInitialized, setIsDataInitialized] = useState(false)

  // Wrap `updateData` in `React.useCallback` to prevent stale data
  const updateData = useCallback(
    (nextData) => {
      // skip the update, no need to update the state with existing data
      if (isDataInitialized) return;
    
      setData({ nodes: nextData.nodes })
      setIsDataInitialized(true) // set to `true` to prevent future updates
    }, 
    [isDataInitialized, setIsDataInitialized]
  )

  const handleReceivedData = useCallback(
    (bytesArray) => {
      const nextData = JSON.parse(...)
      updateData(nextData)
    }, 
    [updateData]
  )

  useEffect(() => {
    window.api.receive('file', handleReceivedData);
  }, [handleReceivedData])
}

Have a look at this example that mimics what you're trying to do:

CodePudding user response:

There some error in you code that i found. When you call updateData(newData), data is null, data.nodes will not work. you can modify you code, "if (data && data.nodes && data.nodes.length !== 1)"

  • Related