Render Does Not Display Changes Until All Promises Are Resolved - reactjs

I am trying to create a React component with a function append that behaves like console.log.
The current situation is that I have a list of URLs to download sequentially and would like to provide the user with progress in real-time.
Here is my component:
async componentDidMount() {
...
for (var i = 0; i < files.length; i++) {
this.downloadFile(files[i].url, files[i].filename);
}
...
}
append(text) {
var newMessage = this.state.message+"\n"+text;
this.setState({message: newMessage});
}
Where downloadFile is an async. I am using Electron and ipcRenderer.sendSync('download-file') returns are promise that resolves when the file has completed saving.
async downloadFile(url, filename) {
await ipcRenderer.sendSync('download-file', {url: url, filename: filename});
this.append("append file: " + filename);
}
When the program runs, React does not display the updated display message until the final ipcRenderer.sendSync promise resolves. I inspected the code and found that componentDidMount() and render() both complete but the component does not actually display any changes until the entire execution completes.
How can I get React to actually render the updated message?
Thank you for reading this.

Related

FabricJS object.set() gives not a function error when loading canvas from json

After performing actions with fabric js, I convert the content of the canvas to json and save it to localstorage by calling saveAsJson() and when the component mounts because I'm using react, I call the loadContentBack() function which loads back perfectly. But when I start to perform actions on the object I get the error, object.set is not a function, and this error only comes up when I load the canvas content from json.
function saveAsJson() {
if (canvas) {
localStorage.setItem("canvasJson", JSON.stringify(canvas.toJSON(["id"])));
}
}
function loadContentBack() {
const jsonData = JSON.parse(localStorage.getItem("canvasJson") ?? JSON.stringify(canvas.toJSON(["id"])));
canvas.loadFromJSON(jsonData, canvas.renderAll.bind(canvas));
}
loadFromJSON accept JSON string or object
loadFromJSON api
function loadContentBack() {
const jsonData = localStorage.getItem("canvasJson") ?? JSON.stringify(canvas.toJSON(["id"]));
canvas.loadFromJSON(jsonData, canvas.renderAll.bind(canvas));
}
enter image description here

this.$refs.selectedImages.files.map is not a function - vue electron

I'm trying to use map function in my vue component. I have a file upload input that will accept multiple files. Since I'm working with electron, I can't pass directly the files to the backend of the app so I want to use the Array.prototype.map() function to get files path and proces them after passing the array using the ipcRenderer.send() function. Anyway I get always this error this.$refs.selectedImages.files.map is not a function how I can fix it?
Here is the actual code I'm trying
handleFiles() {
this.files = this.$refs.selectedImages.files.map( (file) => file.path ); //this will cause the error
}, //
processFiles() {
this.isLoading = true;
window.ipcRenderer.send('processFiles', this.files);
window.ipcRenderer.on('', (event, data) => {
console.log(ecent, data);
saveAs(data, 'processed.mfs')
});
}
UPDATE
To try solve the problem I'm looping the FilesList to extract the path of each file and then pass it using ipcRenderer. The problem is that I'm not able to get the message on the background.js and I'm not sure why.
this.$refs.selectedImages.files.forEach( (file) => {
this.files.push(file.path) // this will push into the array the path of each file
})
window.ipcRenderer.send('processFiles', this.files);
//background.js
import { ipcMain } from 'electron'
ipcMain.on('processFiles', (event, data) => {
console.log(event, data) // the message will not arrive
})
The FileList object is actually not an array, and has no .map method. But you can access the files like this:
const files = this.$refs.selectedImages.files;
for (var i = 0; i < files.length; i++) {
console.log(files[i]);
}

setState() causes state variable to be undefined

I am, for the most part, following this tutorial.
My Django API's set up well. I have this service function:
export default class GoalService{
getGoals() {
const url = `${API_URL}/api/goals`;
return axios.get(url).then(response => response.data);
}
}
Which is called by the componentDidMount method in my GoalList:
class GoalTable extends Component {
constructor(props) {
super(props);
this.state = {
goals: [],
now: now.getDate(),
}
}
componentDidMount() {
var self = this;
goalService.getGoals().then(function (result) {
console.log(result);
self.setState({ goals: result.data })
});
}
render() { ... }
(This is step 8 of the above-linked tutorial).
Now, when I try to use { this.state.goals.map(...) }, I get the error TypeError: this.state.goals is undefined. Looking at other threads, a lot of people seem to have had this problem—but it comes about because they've used setState() outside of the request being made and, since setState() is asynchronous, the state is set to something blank. I'm using it inside of a call to then, so I don't think that's the issue.
I tried adding a second argument to then (in case this operation wasn't successful), but, the getGoals() call is successful, and successfully prints out the JSON sent back by Django's API. Similarly, I can see that the request went as expected in the Network tab of the developer tools.
What could be going wrong here? Why isn't the state properly updating w/ the returned JSON?
As mentioned in the comments, the tutorial has a typo, which means that the code tries to access response.data.data instead of response.data.
The fix would be to remove this extra level of drilling down into the object:
componentDidMount() {
var self = this;
goalService.getGoals().then(function (result) {
self.setState({ goals: result }) // no .data
});
}
Also, note that you could make this code simpler by using arrow functions (which automatically bind the this from the place that they're defined) and the object initialization shorthand:
componentDidMount() {
// { goals } is the same as { goals: goals }
goalService.getGoals().then(goals => this.setState({ goals }));
}

Can't use "this" in stomp client subscribe - React

I have my Spring-Boot service setup so I can send messages through websocket to my browser and it works.
//#MessageMapping
#RequestMapping(value = "/notify")
#SubscribeMapping("/notification")
#SendTo("/topic/notification")
public String sendNotification() throws Exception {
sendMessage();
return "Request to update Tanks has been sent!";
}
public void sendMessage() {
this.messagingTemplate.convertAndSend("/topic/notification", "IT WORKS");
}
Here's the console log from chrome:
<<< MESSAGE
destination:/topic/notification
content-type:text/plain;charset=UTF-8
subscription:sub-1519225601109-13
message-id:f2qodiqn-8
content-length:8
IT WORKS
I want to be able to receive a message from the service and update the state in react, so, that it refetches from the backend. This is what my client looks like:
var socket = new SockJS("http://localhost:6667/refresh");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('connected: ' + frame);
stompClient.subscribe('/topic/notification', function(notification){
console.log(notification.body);
//this.showNotification(JSON.parse(notification.body).content);
//this.showNotification(notification.body);
})
}, function(err) {
console.log('err', err);
});
And the fetch in componentDidMount()
fetch(`http://localhost:6666/front/objects`)
.then(result=>result.json())
.then(fuelTanks=>this.setState({fuelTanks}))
.catch(function(err) {
console.log('Could not fetch: ' + err.message);
}
)
I can't use this.showNotification(notification.body), hence I can't set the state to be able to refetch my objects. I tried making methods outside the class but then I can't use anything from the main class.
Is there a way to make react run componentDidMount again, or better, just access the fetch method in my class when I get a message from spring through the websocket?
Like this:
componentDidMount(){
var socket = new SockJS("http://192.168.1.139:8610/refresh");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('connected: ' + frame);
stompClient.subscribe('/topic/notification', function(notification){
refetchTanks(); // call fetch tanks -> can't use "this"
})
}, function(err) {
console.log('err', err);
});
Thanks!
I know, it is a bit old question, but since it pops every time when you search for stomp issue, i thought of answering it. The way to access this in callbacks is to bind callbacks with this first, then the whole of object can be accessed in the callback.
Example:
connectCallBack(){
this.setState({loading:false})
}
errorCallback=()=>{
}
componentDidMount() {
axios.post('http://localhost:8080/subscribe', null, { params: {
deviceId
}})
.then(response => response.status)
.catch(err => console.warn(err));
const socket = new SockJS('http://localhost:8080/test');
const stompClient = Stomp.over(socket);
//stompClient.connect();
stompClient.connect( {}, this.connectCallBack, this.errorCallback);
If see above code both callbacks can access this.
I tried everything to be able to use my class methods and the state in stompClient's .subscribe method. I was able to connect and reconnect if the service died, nevertheless it wasn't working.
I decided to use react-stomp, which worked. I could use a class method in onMessage=.... This is what my code looks like:
<SockJsClient
url = 'http://localhost:8610/refresh/'
topics={['/topic/notification']}
onConnect={console.log("Connection established!")}
onDisconnect={console.log("Disconnected!")}
onMessage={() => this.update()} <------ this method performs a new GET
request
debug= {true}
/>
I also had to send the message in a specific way on the server side, since I was getting a JSON error when sending a string.
this.messagingTemplate.send("/topic/notification", "{"text":"text"}");
<<< MESSAGE
destination:/topic/notification
content-type:text/plain;charset=UTF-8
subscription:sub-0
message-id:aaylfxl4-1
content-length:49
{
"text": "text"
}
It currently works, but I am curious if there are other, better solutions to this issue.
EDIT: a much better solution here! Use the code from the first post and create a variable before connect to be able to access this like this var self = this;, then just access is as self.update() after subscribe!

How to delay component rendering when response from the server is delayed

I am a newbie to react. I have a situation where in I am fetching some data from the server which I want to display in a page. I am using websockets for communication between the client and server. As the server makes a request to some third party API the response gets delayed. But my component gets rendered before the response comes. I have seen answers which talks about handling such situation in case of ajax request. But how do i handle it in the case of web sockets. My sample jsx page which I want to render after getting response from server is as follows
import React ,{PureComponent} from 'react';
export default class ScorePanel extends PureComponent{
constructor(props){
super(props);
var d = new Date();
this.currentDate = d.getFullYear()+"-"+d.getMonth()+ "-"+ d.getDate();
this.week ="1";
this.numQuarters =1;
}
getInitialState(){
return {
resultsObtianed: false
}
}
getScores(){
return this.props.scores ||[];
}
render() {
return <div className = 'scorePanel'>
if ( !this.state.response ) {
return <div>loging response</div>
}
{
// data to render after geting response from server
}
}
How do I let the client know that response from the server has been received and it's time to render a component. It would be better if I can show a loading page if the response gets delayed. So I would like to make use of getInitialState function as well. I am dispatching an action to the server on a button click in the navigation bar. Thanks for the help in advance
Assuming you have a websocket ws, and listening to "test" event.
in your oncomponentDidMount do
componentDidMount()
{
ws.on("test",(data)=>{
this.setState({response:data})
})
}
also I like to predefine state, so I'd add this.state={response:{}} in constructor

Resources