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