this.funtionName is not defined [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
I am learing to use API an making a news website where you can search a term. I am using the code given below to do so.
var newsAccordion = document.getElementById("newsAccordion");
let news = {
apiKey: "xxxxxxxxxx",
fetchNews: function () {
fetch(
"https://gnews.io/api/v4/top-headlines?&country=in&lang=en&token=xxxxxxxxxxx"
)
.then((response) => response.json())
.then((data) => {
this.fetchCotent(data);
});
},
fetchCotent: (data) => {
console.log(data);
size = data.articles.length;
let newsHtml = "";
for (var i = 0; i < size; i++) {
const { title } = data.articles[i];
const { publishedAt } = data.articles[i];
const { url } = data.articles[i];
const { image } = data.articles[i];
const { description } = data.articles[i];
console.log(title, publishedAt);
var date = new Date(publishedAt).toLocaleString(undefined, {
timeZone: "Asia/Kolkata",
});
}
newsAccordion.innerHTML = newsHtml;
},
searchNews: (term) => {
console.log(term);
fetch(
"https://gnews.io/api/v4/search?&lang=en&q=" +
term +
"&token=xxxxxxxxxx"
)
.then((response) => response.json())
.then((data) => {
this.serchNews();
});
},
searchNews: (term) => {
//code goes here
};
document
.querySelector(".search button")
.addEventListener("click", function (e) {
e.preventDefault();
news.searchNews(document.querySelector(".search-bar").value);
});
window.onload = function () {
news.fetchNews();
};
But the problem is its gaving an error sying
Uncaught (in promise) ReferenceError: showSearch is not defined
at index.js:59
At index.js:59 it says:
Uncaught (in promise) TypeError: this.showSearch is not a function
My question is why is this happening and how can I solve it?
Thanks for any help in advance.

Replace arrow function to regular function for searchNews and showSearch. So that it gets the current scope using this, because arrow function doesn't have it's own context; it takes the context of enclosing function.
searchNews: function(term) {
console.log(term);
fetch(
"https://gnews.io/api/v4/search?&lang=en&q=" +
term +
"&token=368ddd2e4d1c1c559f1fb904cb1e09fa"
)
.then((response) => response.json())
.then((data) => {
this.showSearch(data);
});
},
showSearch: function(data) {
size = data.articles.length;
// your code...
},
Working code example:
const news = {
searchNews: function (term) {
console.log("search news: ", term);
fetchApi("https://gnews.io/api/v4/search")
.then((response) => response.json())
.then((data) => {
console.log("received data:");
this.showSearch(data);
});
},
showSearch: function (data) {
console.log("showSearch: ", data);
// your code...
}
}
function fetchApi(url) { // fetch api stub
return new Promise((resolve, reject) => {
const response = {
data: { id: 123, title: "test-title" },
statusCode: 200
}
let responseObj = new Response(JSON.stringify(response))
setTimeout(resolve(responseObj), 500);
})
}
document.querySelector("#search-button").addEventListener("click", function (e) {
e.preventDefault();
news.searchNews(document.querySelector("#search-bar").value);
});
<html>
<head></head>
<body>
<div>Enter search title:
<input type="text" id="search-bar">
</div>
<div style="margin-top: 20px">
<button id="search-button">Search News</button>
</div>
</body>
</html>

This is because of the misunderstanding of this in Javascript. Please read the this in Javascript.
Anyway you can modify the code as follows.
const obj = {
searchNews: (term) => {
console.log(term);
function cbFunc(data) {
this.showSearch(data);
}
fetch(
"https://gnews.io/api/v4/search?&lang=en&q=" +
term +
"&token=368ddd2e4d1c1c559f1fb904cb1e09fa"
)
.then((response) => response.json())
.then(cbFunc.bind(obj));
},
showSearch: (data) => {
size = data.articles.length;
let newsHtml = "";
for (var i = 0; i < size; i++) {
const { title } = data.articles[i];
const { publishedAt } = data.articles[i];
const { url } = data.articles[i];
const { image } = data.articles[i];
const { description } = data.articles[i];
var date = new Date(publishedAt).toLocaleString(undefined, {
timeZone: "Asia/Kolkata",
});
console.log(title, date, url, image, description);
}
}
}
Please place emphasis on bind call, where obj should be your wrapper object(at the top line of the code).

Related

zxing decodes continuously, when not expected to do so

What follows is the script that is loaded via webpacker in a rails 6 application. It is inspired from this example page's source code
I was under the impression that codeReader.decodeFromVideoDevice (line 72 of example code) implied it would decode only once.
Yes in practice, the scanner keeps decoding until the stop button is activated.
[the non indented code is that which is used for communicating with the application]
Where is this script lacking?
window.addEventListener('turbolinks:load', function () {
let selectedDeviceId;
const codeReader = new ZXing.BrowserMultiFormatReader()
console.log('ZXing code reader initialized')
codeReader.getVideoInputDevices()
.then((videoInputDevices) => {
const sourceSelect = document.getElementById('sourceSelect')
selectedDeviceId = videoInputDevices[0].deviceId
if (videoInputDevices.length >= 1) {
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement('option')
sourceOption.text = element.label
sourceOption.value = element.deviceId
sourceSelect.appendChild(sourceOption)
})
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
const sourceSelectPanel = document.getElementById('sourceSelectPanel')
sourceSelectPanel.style.display = 'block'
}
document.getElementById('startButton').addEventListener('click', () => {
codeReader.decodeFromVideoDevice(selectedDeviceId, 'video', (result, err) => {
if (result) {
console.log(result)
document.getElementById('result').textContent = result.text
let formData = new FormData();
let CodeParams = {
code_data: result.text
};
formData.append("code_json_data", JSON.stringify(CodeParams));
$.ajax({
url: "/home/process_scan_result",
type: "post",
data: formData,
processData: false,
contentType: false
});
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err)
document.getElementById('result').textContent = err
}
})
console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
})
document.getElementById('resetButton').addEventListener('click', () => {
codeReader.reset()
document.getElementById('result').textContent = '';
console.log('Reset.')
})
})
.catch((err) => {
console.error(err)
})
})

useEffect efficiency in Star Wars API

I need some help with me current project making in React. I'am making a star-wars-app for my job interview and I stucked on a one problem.
Fetch efficiency.
I'am fetching this data, and then fetching some more because of the url's in the first fetched data, and everything fetches good, but first i have the 'url's' seeing in the table and then it changes into correct data.
I want to set the 'fetched' state to true when everything is rendered correctly but I don't know how to do it.
const api = `https://swapi.dev/api/people/`;
const [characters, setCharacters] = useState([]);
const [speciesOptions, setSpeciesOptions] = useState([]);
const [homeworldOptions, setHomeworldOptions] = useState([]);
const [fetched, setFetched] = useState(false);
useEffect(() => {
const fetchedTimeout = () => {
setTimeout(() => {
setFetched(true);
}, 2000);
};
const fetchArray = (array, arrName) => {
for (let elem of array) {
fetch(elem).then((response) =>
response.json().then((data) => {
array.shift();
array.push(data.name);
})
);
}
if (arrName === "species") {
if (!array.length) {
array.push("Unspecified");
}
}
};
async function fetchOtherData(characters) {
await characters.forEach((character) => {
const homeworld = character.homeworld;
const vehicles = character.vehicles;
const starships = character.starships;
const species = character.species;
fetch(homeworld).then((response) =>
response.json().then((data) =>
setCharacters((prevData) =>
prevData.map((prevCharacter) =>
prevCharacter.homeworld === homeworld
? {
...prevCharacter,
homeworld: data.name,
}
: prevCharacter
)
)
)
);
fetchArray(vehicles);
fetchArray(starships);
fetchArray(species, "species");
});
await setCharacters(characters);
await fetchedTimeout();
}
const fetchAllCharacters = (allCharacters, data) => {
if (data.next) {
fetch(data.next)
.then((response) => response.json())
.then((data) => {
allCharacters.push(...data.results);
fetchAllCharacters(allCharacters, data);
});
}
if (!data.next) {
fetchOtherData(allCharacters);
}
};
async function fetchApi() {
const allCharacters = [];
await fetch(api)
.then((response) => response.json())
.then((data) => {
allCharacters.push(...data.results);
fetchAllCharacters(allCharacters, data);
})
.catch((error) => console.log(error));
}
const setSpeciesFiltering = () => {
const speciesFiltering = [];
for (let character of characters) {
const characterSpecies = character.species.join();
const foundSpecies = speciesFiltering.indexOf(characterSpecies);
if (foundSpecies === -1) {
speciesFiltering.push(characterSpecies);
}
}
const speciesOptions = speciesFiltering.map((species) => (
<option value={species}>{species}</option>
));
setSpeciesOptions(speciesOptions);
};
const setHomeworldFiltering = () => {
const homeworldFiltering = [];
for (let character of characters) {
const characterHomeworld = character.homeworld;
const foundSpecies =
homeworldFiltering.indexOf(characterHomeworld);
if (foundSpecies === -1) {
homeworldFiltering.push(characterHomeworld);
}
}
const homeworldOptions = homeworldFiltering.map((homeworld) => (
<option value={homeworld}>{homeworld}</option>
));
setHomeworldOptions(homeworldOptions);
};
fetchApi();
setSpeciesFiltering();
setHomeworldFiltering();
}, []);
I will appreciate your response.
Okay, after all the comments (thanks for that!) i changed the code to something like this.
useEffect(() => {
const fetchOtherData = (characters) => {
const charactersWithAllData = [];
characters.forEach((character) => {
const homeworld = character.homeworld;
const species = character.species;
const vehicles = character.vehicles;
const starships = character.starships;
let urls = [homeworld, ...species, ...vehicles, ...starships];
Promise.all(
urls.map((url) => {
if (url.length) {
fetch(url)
.then((response) => response.json())
.then((data) => {
if (url.search("species") > 0) {
character.species = data.name;
}
if (url.search("planets") > 0) {
character.homeworld = data.name;
}
if (url.search("vehicles") > 0) {
character.vehicles.shift();
character.vehicles.push(data.name);
}
if (url.search("starships") > 0) {
character.starships.shift();
character.starships.push(data.name);
}
})
.catch((err) => console.error(err));
}
if (!url.length) {
if (url.search("species")) {
character.species = "Unspecified";
}
if (url.search("vehicles")) {
character.vehicles = "";
}
if (url.search("starships")) {
character.starships = "";
}
}
})
).then(charactersWithAllData.push(character));
});
return charactersWithAllData;
};
const fetchApi = () => {
const characters = [];
Promise.all(
[api].map((api) =>
fetch(api)
.then((response) => response.json())
.then((data) => characters.push(...data.results))
.then((data) => {
setCharacters(fetchOtherData(characters));
})
)
);
};
fetchApi();
}, []);
In what point do i have to set the 'characters' state ? Because in the situation above the data first shows on the screen, and then the state is set.
As other comments say, using Promise.all and refactoroing your useEffect is best solution for this.
But this might be helpful if you don't want to change a lot.
(but still consider refactor your hook)
const [loading, setLoading] = useState(0);
const isLoading = loading > 0;
// replace your fetches with below:
const myFetch = async (path) => {
try {
setLoading(loading => loading + 1);
return await fetch(path);
} finally {
setLoading(loading => loading - 1);
}
};
useEffect(() => {
// do your stuffs
}, []);

Cancelling my axios call on ReactJS but not working

I'm working on a project with graphs and I need to be able to cancel my requests if the user selects a different tab.
Here's my API call
export const getDifferentialData = (
sourceId: string,
sourceLine: string,
source: any
) => {
const graph1Request = getData(
sourceId,
sourceLine,
source
)
const graph2Request = getData(
sourceId,
sourceLine,
source
)
return Promise.all([graph1Request, graph2Request]).then(results => {
const [graphA, graphB] = results
return {
graphA: parsedData(graphA),
graphB: parsedData(graphB),
}
})
}
export const getData = (
sourceId: string,
sourceLine: string,
source?: any
) => {
if (sourceId && sourceLine) {
return api.get(`apiGoesHere`, { cancelToken: source.token }).then(response => {
const { data } = response
return parsedData(data)
})
} else {
return api.get(`apiGoesHere`, { cancelToken: source.token }).then(response => {
const { data } = response
return parsedData(data)
})
}
}
And the component where I'm doing the call. userDidChangeTab is called when pressing on a tab and it calls fetchGraph
const Graph: FC<Props> = () => {
const source = axios.CancelToken.source();
// we ensure that the query filters are up to date with the tab selected
const userDidChangeTab = (tabIndex: number) => {
const isDifferentialTabSelected = isDifferentialTab(tabIndex)
let newFilters = queryFilters
if (isDifferentialTabSelected) {
newFilters = {
// props go here
}
} else {
newFilters = {
// props go here
}
}
source.cancel()
fetchGraph(isDifferentialTabSelected)
setActiveTab(tabIndex)
}
// Function to fetch two differential graphs.
const fetchGraph = (isDifferential: boolean) => {
setFetching(true)
if (isDifferential) {
getDifferentialData(
sourceId,
sourceLine,
source
)
.then(({ graphA, graphB }: any) => {
setGraphData(graphA)
setMatchData(new diffMatch(graphA, graphB, 1.0))
})
.catch(reason => {
const errorMessage = errorMessageFromReason(reason)
addMessageToContainer(errorMessage, true)
})
.finally(() => {
setFetching(false)
})
} else {
getGraph(
sourceId,
sourceLine,
source
)
.then((graphData: any) => {
setGraphData(graphData)
setMatchData(null)
})
.catch(reason => {
const errorMessage = errorMessageFromReason(reason)
addMessageToContainer(errorMessage, true)
})
.finally(() => {
setFetching(false)
})
}
}
}

Try download img from firebase storage which link is firebase database

I tried to download the image which is in firebase storage which link is store in database. When I tried to download the image, it takes more time to execute while for loop is completed.
Is there any process that somehow I download in time which doesn't make the function really slow? I already solve this issue using setTimeout but I hope there may be a better solution than mine. Help me! thank you!
export const shampooHandler = () => {
return (dispatch) => {
dispatch(shampooStart());
const data = [];
const imgList = [];
fire
.database()
.ref()
.child("Shampoo")
.once("value")
.then((response) => {
for (let i = 0; i < response.val().length; i++) {
fire.storage().refFromURL(response.val()[i].img).getDownloadURL().then((image) => {
imgList.push(image);
})
.catch((error) => {
dispatch(shampooError(error));
});
setTimeout(() => {
name = response.val()[i].name;
description = response.val()[i].description;
value = response.val()[i].value;
img = imgList[i];
data.push({ name, description, value, img });
if (i === (response.val().length - 1)) {
dispatch(shampooSuccess(data));
}
}, 3000);
}
})
.catch((error) => {
dispatch(shampooError(error));
});
};
};
I spend a day finding a right solution for it. It may help someone to find solution in future. Thanks guys for giving a thought and specially DougStevensen to tiggering me an idea
export const shampooHandler = () => {
return (dispatch) => {
dispatch(shampooStart());
const data = [];
const imglist = [];
fire.database().ref().child("Shampoo").once("value").then((response) => {
response.val().forEach(element => {
const promise = imageUrlHandler(element.img).then(url => {
return url;
}).catch(error =>{
dispatch(shampooError(error));
})
imglist.push(promise);
//all the promise call to download the images
Promise.all(imglist).then(items =>{
const dataCollection = {
name: element.name,
description: element.description,
value: element.value,
img: items[items.length - 1]
}
data.push(dataCollection);
if(data.length === response.val().length){
dispatch(shampooSuccess(data));
}
}).catch(err =>dispatch(shampooError(err)));
})
}).catch(error => {
dispatch(shampooError(error));
})
}
}
export const imageUrlHandler = (databaseUrl) => {
return new Promise((resolve,reject)=> {
fire.storage().refFromURL(databaseUrl).getDownloadURL().then((url) => {
resolve(url);
})
.catch((error) => {
reject(error)
});
})
}

ReactJs Onclick Not Working If Append To Table in Javascript

I'm pushing JsonArray data to datatable in reactjs through array as following.
tableTest= () => { //Function Name
axios({
method: 'post',
url: "/test" //URL
})
.then(response => {
var testArray= [];
var slNo = 0;
response.data.map((item, index) => {
var result = [];
slNo++;
var result = [];
result.push(slNo);
result.push(item.id);
result.push(item.name);
result.push(item.mob);
result.push("<button onclick={this.accept}>Accept</button>");
testArray.push(result);
})
this.setState({ testTableTable: testArray});
}).catch(response => {
console.log("Error", response);
})
}
If I click the button Accept I get "this.accept" is not a function.
Can anyone please tell me how to write onclick function in Javascript.
Thank you :)
Instead of onclick function use column onClick function of datatable the source code as shown below:
tableTest= () => {
axios({
method: 'post',
url: "/test" //URL
})
.then(response => {
var testArray= [];
var slNo = 0;
response.data.map((item, index) => {
var result = [];
slNo++;
var result = [];
result.push(slNo);
result.push(item.id);
result.push(item.name);
result.push(item.mob);
result.push("<button class='accept"+index+"'>Accept</button>");//define class here to use reference for column onClick function
testArray.push(result);
})
this.setState({ testTableTable: testArray});
}).catch(response => {
console.log("Error", response);
})
}
//Change table to Datatable
setDataTableData = () => { //set datatable
this.$el2 = $(this.el2) //el2 is reference of table
this.$el2.DataTable(
{
data: this.state.RowTableData, //RowTableData is array
"columnDefs": [
//column click function goes here
{
"targets": 4,// 4th column in a table
"createdCell": (td, cellData, rowData, row, col) => {
$(td).on('click', '.accept' + row, () => { //Click on <td>
this.accept(); //call function here
})
}
}
],
}
)
}
//Accept function Here
accept =() =>{
//function here
}
.then(response => {
var testArray= [];
window.YOUR_CLICK_FN = this.accept.bind(this);
var slNo = 0;
response.data.map((item, index) => {
var result = [];
slNo++;
var result = [];
result.push(slNo);
result.push(item.id);
result.push(item.name);
result.push(item.mob);
result.push("<button onclick={YOUR_CLICK_FN}>Accept</button>");
testArray.push(result);
})
this.setState({ testTableTable: testArray});
}).catch(response => {
console.log("Error", response);
})
Problem is in refreance binding.
React event handlers are named using camelCase. You should be calling onClick instead of onclick.
See link below:
https://reactjs.org/docs/handling-events.html
CodeSandbox example:
https://codesandbox.io/s/3rp301jjym
tableTest= () => { //Function Name
axios({
method: 'post',
url: "/test" //URL
})
.then(response => {
var testArray= [];
var slNo = 0;
response.data.map((item, index) => {
var result = [];
slNo++;
var result = [];
result.push(slNo);
result.push(item.id);
result.push(item.name);
result.push(item.mob);
result.push((<button onclick={this.accept}>Accept</button>));
testArray.push(result);
})
this.setState({ testTableTable: testArray});
}).catch(response => {
console.log("Error", response);
})
}
Problem is it is stored as string. So you have to push it as a react component to make it work.
And onclick doesn't work in react. You have to use onClick

Categories