why reactjs setState sometimes rerender old state data? - javascript

After Adding data to database with Ajax calls my TodosList component must refresh and show the all todos with the new one added after updating it using another Ajax call (that calls is a php SELECT * statement), but that doesn't happen; sometimes it shows the new data and sometimes only old data until I refresh page or added a new todo.
Isn't the role of setState() is to update our View with NEW DATA? How do I get that to work? (I added a callback to setState() that it's Console.log(this.state.todos) to see what happen but as I mentioned sometimes todos array got updated and sometimes it doesn't it shows only old data until page refreshed manually or added a new todo) I used Axios, and XMLHTTPRequest with promise but same problem .(in console screenshot u can see how array length is not updated sometimes)
constructor(props) {
super(props);
this.state = {
todos: [],
currentTodoText: '',
currentTodoEditText: ''
};
this.todoFormTfHandler = this.todoFormTfHandler.bind(this);
this.todoFormBtnHandler = this.todoFormBtnHandler.bind(this);
this.todoCheckHandler = this.todoCheckHandler.bind(this);
this.todoEditTextHandler = this.todoEditTextHandler.bind(this);
this.todoEditHandler = this.todoEditHandler.bind(this);
this.todoRemoveCancelHandler = this.todoRemoveCancelHandler.bind(this);
this.makeRequest = this.makeRequest.bind(this);
}
//called whenever app started and when a todo is added to database to refresh results
getDataFromDataBase() {
//get all registred todos
//let requestResult = [];
/*const xhr = new XMLHttpRequest();
return new Promise(function(resolve, reject) {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const requestResult = JSON.parse(xhr.responseText);
resolve(requestResult);
/*this.setState({ todos: requestResult });
console.log(requestResult);*/
/* } else {
reject({ status: xhr.status, statusText: xhr.statusText });
}
};
xhr.open('POST', 'http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send();
});*/
//console.log(requestResult);
//this.setState({ todos: requestResult });
axios.post('http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php').then((res) => {
this.setState({ todos: res.data }, () => {
console.log(this.state.todos);
});
});
}
makeRequest = (url, method) => {
// Create the XHR request
var request = new XMLHttpRequest();
// Return it as a Promise
return new Promise(function(resolve, reject) {
// Setup our listener to process compeleted requests
request.onreadystatechange = function() {
// Only run if the request is complete
if (request.readyState !== 4) return;
// Process the response
if (request.status >= 200 && request.status < 300) {
// If successful
resolve(request.responseText);
} else {
// If failed
reject({
status: request.status,
statusText: request.statusText
});
}
};
// Setup our HTTP request
request.open(method || 'GET', url, true);
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
// Send the request
request.send();
});
};
componentDidMount() {
/*this.getDataFromDataBase()
.then(function(data) {
this.setState({ todos: data });
console.log('wttttf');
})
.catch(function(error) {
console.log(error);
console.log('lolo');
});*/
/*
this.makeRequest('http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php', 'POST')
.then((data) => {
this.setState({ todos: data });
})
.catch(function(error) {
console.log('Something went wrong', error);
});*/
this.getDataFromDataBase();
}
todoFormTfHandler(e) {
/*used to save whats writed in the field*/
//const tmpTodoText = e.target.value;
const tmpTodoText = e.target.value;
this.setState({ currentTodoText: tmpTodoText });
}
todoFormBtnHandler(e) {
if (this.state.currentTodoText !== '') {
const tmpTodo = {
//id: this.state.todos.length + 1,
text: this.state.currentTodoText,
isDoneChecked: false,
isEditable: false,
isRemoved: false
};
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
/*if (xhr.readyState === 4 && xhr.status === 200) {
}*/
};
const tmpTodoJSON = JSON.stringify(tmpTodo);
xhr.open('POST', 'http://localhost/React_ToDo_APP/to-do/src/PHPFiles/AddTodo.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send('data=' + tmpTodoJSON);
/*const tmpTodos = this.state.todos;
tmpTodos.push(tmpTodo);
this.setState({ todos: tmpTodos });*/
this.getDataFromDataBase();
/*this.makeRequest('http://localhost/React_ToDo_APP/to-do/src/PHPFiles/SelectAllTodos.php', 'POST')
.then(function(data) {
this.setState({ todos: data });
})
.catch(function(error) {
console.log('Something went wrong', error);
});*/
const tmpTodoText = '';
this.setState({ currentTodoText: tmpTodoText });
}
}

Related

why this call back gives infinity loop

so I'm learning about async-await in javascript and when I try to do this callback hell ( I know it is not the best practice but I have to learn It )
the second time I call the function it keeps calling itself ( infinity loop )
u can run the code to understand more cuz nothing looks wrong to me I spend the last 2 days trying to understand the problem but I end up here
code :
const xhr = new XMLHttpRequest();
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status == 200) {
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
moveiData(url, (response) => {
console.log(response);
});
});
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status == 200) {
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
});
You have three options in that issue.
1- You have added a new event listener and you should remove it when you take response.
const xhr = new XMLHttpRequest();
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
const handler = () => {
if (xhr.readyState === 4 && xhr.status == 200) {
xhr.removeEventListener("readystatechange", handler);
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
};
const a = xhr.addEventListener("readystatechange", handler);
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
moveiData(url, (response) => {
console.log(response);
});
});
2- Use onreadystatechange callback.
const xhr = new XMLHttpRequest();
const url = new URL("https://www.breakingbadapi.com/api/quotes");
const moveiData = (link, callBack) => {
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status == 200) {
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack("could not fetch data");
}
};
xhr.open("GET", link);
xhr.send();
};
moveiData(url, (response) => {
console.log(response);
moveiData(url, (response) => {
console.log(response);
});
});
3- Declare new XMLHttpRequest for every function.
const url = new URL('https://www.breakingbadapi.com/api/quotes');
const moveiData = (link, callBack) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState === 4 && xhr.status == 200) {
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack('could not fetch data');
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, function (response) {
console.log(response);
moveiData(url, function (response) {
console.log(response);
});
});
Don't reuse the same XMLHttpRequest object. Declare a new one every time you call the function, and make sure you return some data in the callback if there's a positive response.
const url = new URL('https://www.breakingbadapi.com/api/quotes');
const moveiData = (link, callBack) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState === 4 && xhr.status == 200) {
callBack(JSON.parse(xhr.response));
} else if (xhr.readyState === 4) {
callBack('could not fetch data');
}
});
xhr.open("GET", link);
xhr.send();
};
moveiData(url, function (response) {
console.log(response);
moveiData(url, function (response) {
console.log(response);
});
});
You may find the relatively new fetch API and the async/await process a little more rewarding.
const url = 'https://www.breakingbadapi.com/api/quotes';
function getData(url) {
return fetch(url);
}
async function main() {
try {
const res = await getData(url);
console.log(await res.json());
} catch (err) {
console.log(err);
}
}
main();

How to send multiple different requests using promises, and have all run one after the other?

I have this script that I use to import some data from an API, and into my database. Since this process is very time consuming, it often times out because on some of the items processed there is a lot of data to process..
I came with this solution a while ago, using promises, to first do the request to the API, then after it finishes I would prepare the data and put it into a temporary csv file, then I would fire another request to split that file into multiple smaller files, then... you get the idea... it was working, but I need to add to it some extra requests, I just can't make it work... I probably just need to simplify my logic.
Anyone can help me improve this code to make it easier to add those extra requests and keep it sending one request after the other?
This is the (over simplified) script in question:
window.importTrialsScripts = {};
window.addEventListener('DOMContentLoaded', function() {
importTrialsScripts.app.initialize();
});
(function(importTrialsScripts, document, $) {
importTrialsScripts = importTrialsScripts || {};
const app = {
ajaxurl: 'myajaxurl',
initialize: function() {
this.submitHandler();
},
submitHandler: function() {
const self = this;
document.querySelector('#start-import').addEventListener('click', function() {
self.pullTrialsFromApi();
});
},
pullTrialsFromApi: function() {
let data = new FormData();
data.append('action', 'pull_trials_from_api');
[123, 456, 789].forEach(function(str) {
data.append('ids[]', str);
});
this.startPullingTrials(data);
},
startPullingTrials: function(data) {
const self = this;
let promise = new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if (this.status === 200) {
const response = JSON.parse(this.response);
if (! response.success) {
alert('The API could not be reached. Please try again.');
console.error('Error!!', response);
return;
}
resolve(response.data);
}
else {
console.error('there was an error in the request', this);
reject(this);
}
});
});
promise.then(function(chunks) {
const processingChunks = Object.values(chunks).map(function(chunk) {
return self.processChunk(chunk);
});
Promise.all(processingChunks).then(function (processedTrials) {
console.log('finished', processedTrials);
});
}, function(err) {
console.error('promise rejected', err);
});
},
processChunk: function(chunkTrials) {
const self = this;
let data = new FormData();
data.append('action', 'process_trials_chunk');
Object.values(chunkTrials).forEach(function(chunk) {
data.append('chunk[]', JSON.stringify(chunk));
});
return new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if (this.status === 200) {
const response = JSON.parse(this.response);
if (! response.success) {
console.error('Error!!', response.data);
return;
}
resolve(response.data);
}
else {
console.log('there was an error in the request', this);
reject(this);
}
});
});
},
splitToMultipleFiles: function() {
const self = this;
const data = new FormData();
data.append('action', 'split_location_files');
return new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if (this.status === 200) {
const response = JSON.parse(this.response);
if ( ! response.success ) {
console.error('Error!!', response.data);
return;
}
resolve(response.data.files);
}
else {
console.log('there was an error in the request', this);
reject(this);
}
});
});
},
processLocation: function(file) {
const self = this;
let data = new FormData();
data.append('action', 'process_location_data');
data.append('file', file);
return new Promise(function(resolve, reject) {
self.sendAjaxRequest(data, function() {
if ( this.status === 200 ) {
const response = JSON.parse(this.response);
if (! response.success) {
console.error('Error!!', response.data);
return;
}
resolve(response.data);
}
else {
console.log('there was an error in the request', this);
reject(this);
}
});
});
},
sendAjaxRequest: function(data, callback) {
const self = this;
let xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl);
xhr.onload = callback;
xhr.addEventListener('timeout', function(e) {
console.error('the request has timed out', e);
});
xhr.addEventListener('error', function(e) {
console.error('the request returned an error', e);
});
xhr.addEventListener('abort', function(e) {
console.error('the request was aborted', e);
});
xhr.send(data);
},
};
$.extend(importTrialsScripts, {
app: app
});
}).apply(this, [window.importTrialsScripts, document, jQuery]);

Promise returning undefined after sending value to the server

I just started making use of the promise function in javascript, and i do like it, but i am running into an issue.
I am getting a value from a promise object, and then sending the response back to the server in an asynchronous promise function. When i send the value back to the server, and try to see what the value is, it returns undefined.
var xhr = new XMLHttpRequest();
function createFirstChannel(method, rippleApi) {
return new Promise(function (resolve, reject) {
xhr.open(method, url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onload = function () {
if (xhr.readyState == 4 ) {
var hashValue = resolve(JSON.parse(xhr.responseText));
}
else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send(json);
});
}
A value gets returned from the first function which i am using in the next promise function
async function createChannel(method, url) {
var datums = await createFirstChannel(method, url);
//method to return hash value from json request
for (var key in datums) {
var tx_json = datums[key]['json'];
datums = (json['hash']);
console.log(datums);
}
//creating the json request to send back out again using POST METHOD
var channel= {
"method": "tx",
"par": [{
"transaction": datums
}]
}
var jsonPayee = JSON.stringify(channel);
xhr.open(method, url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onload = function () {
if (xhr.readyState == 4) {
users = JSON.parse(xhr.responseText);
}
};
xhr.send(jsonPayee);
return users;
}
createChannel(method, url).then(datums => {
console.log(datums);
}).catch(function (err) {
console.error('Sorry There Was An Error!', err.statusText);
});
i get "undefined" response at console.log. is there any way to resolve this error? Thanks for the help

Add Cache-Control and Expires headers to Azure Blob Storage Node JS

I'm using Azure Storage to serve up static file blobs but I'd like to add a Cache-Control and Expires header to the files/blobs when served up to reduce bandwidth costs.
But I am using Node JS
How I do this?
This is what I've until now:
Simpler solution: jsfiddle.net/fbqsfap2
//https://myaccount.blob.core.windows.net/mycontainer/myblob?comp=properties
function updateBlob(blobName, containerName, accountName, properties) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
console.log(xmlhttp.status);
if (xmlhttp.status == 200) {
const updatedPropertiesStr = getPropertiesKeysStr(properties);
console.log("Update sucessfully ", updatedPropertiesStr);
}
else if (xmlhttp.status == 400) {
console.log('There was an error 400');
}
else {
console.log('Something else other than 200 was returned' , xmlhttp.status);
}
}
};
const url = "https://<account>.blob.core.windows.net/<container>/<blob>?cache-control='max-age=3600'";
xmlhttp.open("PUT", url, true);
xmlhttp.send();
}
function updateBlobPorperty(blobName, containerName, accountName, properties) {
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
console.log(xmlhttp.status);
if (xmlhttp.status == 200) {
const updatedPropertiesStr = getPropertiesKeysStr(properties);
console.log("Update sucessfully ", updatedPropertiesStr);
}
else if (xmlhttp.status == 400) {
console.log('There was an error 400');
}
else {
console.log('Something else other than 200 was returned' , xmlhttp.status);
}
}
};
const getParameters = object2getParameters(properties);
const url = `https://${accountName}/${containerName}/${blobName}?${getParameters}`;
xmlhttp.open("PUT", url, true);
xmlhttp.send();
}
function getPropertiesKeysStr(properties){
return Object.keys(properties).reduce((actual, next) => actual.concat(` ${next}`), "");
}
function object2getParameters(object){
let propertiesStr = "";
for (key of Object.keys(object)) {
let prop = `${key}=${object[key]}&`;
propertiesStr += prop;
}
propertiesStr = propertiesStr.substr(0, propertiesStr.length-1);
return propertiesStr;
}
Complete Solution using Node JS: jsfiddle.net/rhske0nj
const azure = require('azure-storage');
const got = require('got');
const Promise = require('promise');
// =============================== Consts Definitions ====================================
const AZURE_STORAGE =
{
ACCOUNT: "ACCOUNT",
ACCESS_KEY: "ACCESS_KEY"
}
const blobService = azure.createBlobService(AZURE_STORAGE.ACCOUNT, AZURE_STORAGE.ACCESS_KEY);
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// =============================== API to Azure Functions ================================
function getBlobsName(blobService, containerName){
return new Promise(function(resolve, reject){
blobService.listBlobsSegmented(containerName, null, function(err, result) {
if (err) {
reject(new Error(err));
} else {
resolve(result.entries.map(BlobResult => BlobResult.name))
}
})
});
}
function getContainersName(blobService){
return new Promise(function(resolve, reject){
blobService.listContainersSegmented(null, function(err, result) {
if (err) {
reject(new Error(err));
} else {
resolve(result.entries.map(ContainerResult => ContainerResult.name));
}
})
});
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// =================================== Main Function =====================================
function update(blobService){
getContainersName(blobService)
.then(containersName =>
containersName.forEach(cn =>
getBlobsName(blobService, cn)
.then(BlobsName =>
BlobsName.forEach(bn =>
updateBlobPorperty(bn, cn, AZURE_STORAGE.ACCOUNT,
{
headers: {
"Cache-Control": "max-age=2592000"
}
}
)
.then(console.log("Sucessfully updated"))
.catch(console.log)
//.cacth(console.log("Something failed"))
)
//console.log
)
.catch(err => console.log(err))
)
)
.catch(err => console.log(err))
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
update(blobService);
// =============================== Update Blob Block =====================================
/*
Example of Url:
https://homologsquidmedia.blob.core.windows.net/squid-pic/1.jpg?cache-control=max-age=3600
*/
function updateBlobPorperty(blobName, containerName, accountName, properties){
const cdnFileUrl = `https://${accountName}.azureedge.net/${containerName}/${blobName}`;
const fileUrl = `https://${accountName}.blob.core.windows.net/${containerName}/${blobName}`;
console.log(`fileUrl:> ${fileUrl}`);
//return got.put(fileUrl, properties);
return got.put(fileUrl, properties);
//return got.put(fileUrl);
}
function getPropertiesKeysStr(properties){
return Object.keys(properties).reduce((actual, next) => actual.concat(` ${next}`), "");
}
function object2getParameters(object){
let propertiesStr = "";
for (key of Object.keys(object)) {
let prop = `${key}=${object[key]}&`;
propertiesStr += prop;
}
propertiesStr = propertiesStr.substr(0, propertiesStr.length-1);
return propertiesStr;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I need to update for all my blobs in all my containers the property cache-control. If more information are needed please tell me. Tks.
The function you would want to call is setBlobProperties. This would set user-defined properties for the specified blob or snapshot.
Here is a sample code snippet that you can use in your application to persist the new Cache-Control value.
var properties = {};
properties.cacheControl = 'max-age=2592000';
blobService.setBlobProperties('<containerName>', '<blobName>', properties, function(error, result, response) {
if(!error) {
console.log('blob properties setted!');
}
})
The code will set the following properties for the Blob.

How would you test with Jasmine an Vanilla JS Ajax call that is done with a Promise?

I come from here: How to make an AJAX call without jQuery?
And it is difficult to mock the Promise, the AJAX call or both.
I have tried so far with jasmine-ajax but it seems to have a bug... and apparently "is only meant for browser environment".
I have also tried to mock the XMLHttpRequest object but without success.
So, I am not sure what my options are here:
function get(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('GET', url);
req.onload = () => req.status === 200 ? resolve(req.response) : reject(Error(req.statusText));
req.onerror = (e) => reject(Error(`Network Error: ${e}`));
req.send();
});
}
Proof of concept how XMLHttpRequest can be mocked
function get(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('GET', url);
req.onload = () => req.status === 200 ? resolve(req.response) : reject(Error(req.statusText));
req.onerror = (e) => reject(Error(`Network Error: ${e}`));
req.send();
});
}
describe('XMLHttpRequest', function() {
var xhr
var urls
beforeEach(() => {
xhr = {
open: (_type_, _url_) => {
xhr.status = urls[_url_].status
xhr.response = urls[_url_].response
xhr.statusText = urls[_url_].statusText
},
send: () => {
setTimeout(xhr.onload, 1)
}
}
XMLHttpRequest = jasmine.createSpy(xhr).and.returnValue(xhr)
})
it('resolves query with `response`', function(done) {
urls = {
'http://foo/bar': {
response: 'some other value',
status: 200
}
};
get('http://foo/bar')
.then(r => expect(r).toBe('some other value'), e => expect(e).toBe(undefined))
.then(done)
})
it('rejects query with `statusText`', function(done) {
urls = {
'http://baz/quux': {
response: 'some other value',
status: 500,
statusText: 'some error'
}
};
get('http://baz/quux')
.then(r => expect(r).toBe(undefined), e => expect(e.message).toBe('some error'))
.then(done)
})
})
<script src="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<link href="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />

Categories