React native, creating buttons in for loop - javascript

I am trying to get some data from API of eventbrite.
The data is event's name, and the name will be inserted into the list.
In render, buttons are created as many as the number of name which I got from API
I have got few questions below..
How to add information from API into array list - so I can use index, and value.
How to create buttons in forloop
e.g.
for ( var i =0; i<5; i++){
<Button
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"/>
} // % buttons are created.
This is my code.
export const renderButtons1 = (numOfBtns,title,site,navigated) => {
const views1 = [];
for ( var i = 0; i < numOfBtns; i++) {
views1.push(
<Button
onPress={(i) => navigate('EventsList', {
Title: title[i]
})
}
title = {title[i]}
color="#841584"
/>);
}
componentDidMount(){
return fetch('https://www.eventbriteapi.com/v3/events/search/location.address=glasgow&token=F7AWKEHKD6BW2TZKWO7N&expand=venue')
.then((response) => response.json())
.then((responseJson) => {
for(var x in responseJson.events){
this.setState({
state : this.state[Events].push(responseJson.events[x][0][0]["text"],"\n",)
});
}})
.catch((error) => {
console.error(error);
});
}
render() {
need to make buttons as many as the number of gotten name from API
}

For question 1, array form of api data will depend on how the data is structured before being sent to your application. You may have to shade more light on that.
Rendering views in a loop, try that
const renderButtons = () => {
const views = [];
for ( var i =0; i<5; i++){
views.push(
<Button
key={i}
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>);
} // % buttons are created.
return views;
}
call renderButtons() in your render methods.

Related

React - parent passing data to child becomes undefined

I am fetching an object named Quiz. Im trying to pass a single element of Quiz (questionAnswerPair) to a child element named QuestionAnswer.
The Quiz is collected fine and each element is able to iterate through. However the integration doesn't work as intended currently though that is not the reason of my post.
The reason for my post is the QuestionAnswerPair is not being passed to the child element correctly (displayed undefined when printed in child component).
parent:
export default function Quiz() {
var passedid = 1; //need to replace this with passed quiz ID
const [quiz, setQuiz ]=useState('')
const [executedFetch, setExecutedFetch]=useState('')
useEffect(() => {
fetch(`http://XXX.XXX.XXX.XXX:8080/Quiz/${passedid}`)
.then(response => response.json())
.then(json => {
console.log(json)
setQuiz(json)
setExecutedFetch(true)
})
},[])
const [currentQuestionAnswerPair, setCurrentQuestionAnswerPair]=useState('')
const [executedIterate, setExecutedIterate]=useState('')
//this runs twice, once before the data collection and
//one after which is why it needs the conditional if
useEffect(() => {
if (executedFetch === true){
for (var i=0; i < quiz.questionAnswerPairs?.length; i++ )
{
console.log(quiz.questionAnswerPairs[i]) //prints current fine
setCurrentQuestionAnswerPair(quiz.questionAnswerPairs[i])
setExecutedIterate(true)
// make it wait till the question is answered somehow
// then store the answer within an attempt
}
//set entire attempt here
}
},[executedFetch])
const[ userSelectionData, setUserSelectionData]=useState('')
const childToParent = (userSelectionData) => {
setUserSelectionData(userSelectionData);
}
const parentToChild = () => {
setParentToChildData(currentQuestionAnswerPair);
}
if (executedIterate === true){
console.log("executedIterate " + executedIterate)
console.log(currentQuestionAnswerPair)
return (
<br/> <br/> <br/> <br/>
parent data = {userSelectionData}
<br/> <br/> <br/> <br/>
<QuestionAnswer parentToChild={currentQuestionAnswerPair} childToParent={childToParent}/>
</div>
);
}
else{
return (
<div className="App">
not displaying
</div>
);
}
}
child:
export default function QuestionAnswer({currentQuestionAnswerPair,childToParent}) {
const userSelectionData = "child data passed"
const [questionAnswer, setQuestionAnswer]=useState('')
useEffect(() => {
console.log(currentQuestionAnswerPair) // this is undefined
setQuestionAnswer(currentQuestionAnswerPair)
},[])
console.log(currentQuestionAnswerPair) // this is undefined
console.log(questionAnswer) // this is undefined
return (
<div>
<br/>
current question answer = {questionAnswer}
<br/> <br/>
current question answer pair = {currentQuestionAnswerPair}
<br/> <br/>
<Button primary onClick={() => childToParent(userSelectionData)}>Click Child</Button>
</div>
)
}
Furthermore passing dependency to the child useEffect [currentQuestionAnswerPair] doesn't change the issue
output in console:
change this line,
<QuestionAnswer parentToChild={currentQuestionAnswerPair} childToParent={childToParent}/>
to this :
<QuestionAnswer currentQuestionAnswerPair={currentQuestionAnswerPair} childToParent={childToParent}/>
As you are passing the props, parentToChild and childToParent, not currentQuestionAnswerPair and childToParent, the prop currentQuestionAnswerPair is always undefined

Using axios doesn't rerender my React Native component

I am using hooks in React Native. This is my code:
useEffect(() => {
if (var1.length > 0 ){
let sym = [];
var1.map((items) => {
let count = 0;
state.map((stateItems) => {
if(items === stateItems.name) {
count = count + 1;
}
})
if (count === 0) {
sym.push(items)
}
});
async function getAllStates (sym) {
let stateValues = [];
await Promise.all(sym.map(obj =>
axios.get(ServerURL + "/note?name=" + obj).then(response => {
stateValues.push(response.data[0]);
})
)).then(() =>{
setNewItem(stateValues);
});
}
getAllStates (sym);
}
}, [var1]);
useEffect(() => {
let stateValues = state;
for( let count = 0 ; count < newItem.length; count++ ){
stateValues.push(newItem[count]);
}
setState(stateValues);
}, [newItem]);
This runs successfully without any errors. However, when the state is displayed as below, I am not seeing the latest value added in the state. It shows all the previous values. When I refresh the whole application, I am able to see my value added.
return (
<View style={styles.container}>
<Text style = {{color:"white"}}>
{
state.map( (item, key) =>{
return(
<Text key = {key} style = {{color:"white"}}> {item.name} </Text>
)
})
}
</Text>
</View>
);
Can someone tell me why this is happening? I want to see the data render immediately after the axios call. I am using React Native.
when i force update using :stackoverflow.com/questions/53215285/... it works fine. However, i am looking for a better fix if anyone can provide?
This should do:
useEffect(() => {
var1.forEach(async (name) => {
if (state.some(item => item.name === name)) return;
const response = await axios.get(ServerURL + "/note?name=" + name);
setState(state => [...state, response.data[0]]);
});
}, [var1]);
I still see two issues in your approach:
this code may start the same ajax request multiple times before the result of the firstz ajax-request is added to state; this also means that the result for the same name may be added multiple times to state.
for each item of var1 times each item of state, this is an O(n*m) problem or in this case basically O(n²) as m is pretty fast catching up to n. You should find a better approach here.
And I'm almost certain that [var1] is wrong here as the dependency for useEffect. But you'd need to show where this value comes from to fix that, too.

Stop function while running in React

Introduction
I am building a sorting algorithms visualizer for studying purposes, and i have a sortArray() function that takes care of animating the array being sorted. As you can see in the code.
and it works fine, but the problem is that i wanted to give the array takes some time to get sorted, and i wanted a way to stop this function while running.
What I've Tried So Far
After i realised that i had this problem, i thought that a "Stop" button would be a good way to solve my problem i added a new state called "Abort", and the original idea was that while the sortArray() function is running, the function would check everytime if the abort state is set to true, and if it is, the function would stop. and of course, it didn't work.
after that my previous attempt had failed, i deleted the "abort" state from the code and transformed it into a normal variable, thinking that the problem maybe was related to the setState function being async, and it didn't work either.
after that i did some research, but i didn't found anything useful.
The Code:
resetArray(){
const arrayToSort = [];
const prevChanged = [];
for (let i = 0; i < this.state.numberOfItems; i++) {
arrayToSort.push(this.RandomIntBetweenRange(5, 1000));
}
this.setState({ arrayToSort, prevChanged, abort: false });
}
generateNewArray(){
this.setState({abort: true},{
this.resetArray();
});
}
async SortArray(algo){
let sortedArrayAnim = algo(this.state.arrayToSort);
let arrayToSort = this.state.arrayToSort;
let prevChanged = this.state.prevChanged;
for (let index = 0; index < sortedArrayAnim.length; index++) {
const [i,j] = sortedArrayAnim[index];
if(this.state.abort){
console.log(abort);
return null;
}
let temp = arrayToSort[i];
arrayToSort[i] = arrayToSort[j];
arrayToSort[j] = temp;
prevChanged.push(i,j);
if(index == sortedArrayAnim.length - 1){
prevChanged.push(arrayToSort.length + 1, arrayToSort.length + 1);
this.setState({prevChanged});
}
this.setState({ arrayToSort,prevChanged });
await sleep(10);
}
}
render() {
const {arrayToSort} = this.state;
return (
<div className="main-div">
{arrayToSort.map((value, idx) => (
<div className="array-item" key={idx} style={{height: value, width: 800 / this.state.numberOfItems, backgroundColor: this.getColor(idx)}}>
</div>
))}
<button onClick={() => this.generateNewArray()}>Generate new array</button>
<button onClick={() => this.SortArray(BubbleSort)}>Bubble Sort</button>
<button onClick={() => this.SortArray(InsertionSort)}>Insertion Sort</button>
<button onClick={() => this.SortArray(QuickSort)}>Quick Sort</button>
<input type="number" min="5" max="1500" onChange={(event) => this.handleOnChange(event)} defaultValue={this.state.numberOfItems}/>
</ div>
);
}
Could you do something involving a Web-worker?
Structure the class something like this
class LongOperation extends React.Component {
constructor(props) {
super(props)
this.state = {
running: false,
results: null,
worker: null,
}
}
startLongOp(){
const worker = new Worker('yourIsolatedOp.js')
//...setup
this.setState({
running: true,
worker: worker,
})
}
cancel() {
if(running) {
this.state.worker.terminate()
}
this.setState({
running: false,
worker: null,
})
}
render() {
//Do your rendering
<button onClick={e => this.startLongOp()}>Start</button>
<button onClick={e => this.cancel()}>Cancel</button>
}
}
//while loop:
while(this.state.running) {
//do stuff
}
You could also probable do it with a while loop, but it would be on the UI thread. I have not tried this, it is just a sketch from the top of my head. Now designing a concurrent sort is going to be the bigger challenge... Workers are going to be the easiest if you can isolate it.
*note: I didn't go into details on how to deal with the web-worker passing data etc.. but you can easily find it. I see no reason you can't update the state from these messages.

React app need to fill an array in a for loop

I was filling an array directly from a promise, but then I realized I need to add some html to the array so I was attempting to use a for loop.
pullData().then((response) => {
//previously populated medication array from response like this
this.setState({medications: response});
//now I wish to loop how can I put data in , push data in?
for (let i = 0; i < response.length; i++) {
// push data into array?
this.medications.push <a onclick={`jsfunction(${response[i].value}) href=javascript:void(0);`}>{`${response[i].value}`}</a>;
}
});
I realize there are map functions, but I just want to get data into my array. there are two columns, "value" and "short_description"
The problem that you are mentioning seems unclear. As per my understanging, you want to fetch those data and show it as a list.
Here try this one
pullData().then((response) => {
let adding_list=[];
//if you want to append the response in previous list uncomment below line
let adding_list=this.state.medications;
//beleiving that the response is an array
response.map((item)=>{
adding_list.push( <a onclick={`jsfunction(${item.value}) href=javascript:void(0);`}>{`${item.value}`}</a>);
}
this.setState({medications:adding_list});
});
You cant just update state by calling this.medications.push()
You can update state inside for loop like this:
for (let i = 0; i < response.length; i++) {
let newMedication = <a onclick={`jsfunction(${response[i].value}) href=javascript:void(0);`}>{`${response[i].value}`}</a>;
this.setState(prevState => {
return {
medications: [...prevState.medications, newMedication]
}
})
}
Or you can save the new medications to a new array and then update state:
let allMedications = [];
for (let i = 0; i < response.length; i++) {
allMedications.push(<a onclick={`jsfunction(${response[i].value}) href=javascript:void(0);`}>{`${response[i].value}`}</a>);
}
this.setState({
medications: allMedications
})
I ended up doing this
pullData().then((response) => {
const modifiedResponse = response.map((item) => {
const modifiedValue = <a onClick={() => this.jsfunction(item.value, item.short_description)} href="javascript:void(0);">{item.value}</a>;
item.value = modifiedValue;
return item;
});
this.setState({medications:modifiedResponse});
});

React Axios API call with array loop giving wrong order?

I was learning react and doing some axios api call with an array. I did a code on gathering data through coinmarketcap api to learn.
So, my intention was to get the prices from the api with a hardcoded array of cryptocurrency ids and push them into an array of prices. But I ran into a problem with the prices array, as the prices were all jumbled up. I was supposed to get an array in this order
[bitcoinprice, ethereumprice, stellarprice, rippleprice]
but when I ran it in the browser, the prices came randomly and not in this order, sometimes I got my order, sometimes it didn't. I used a button which onClick called the getPrice method. Does anyone know what went wrong with my code? Thanks!
constructor(){
super();
this.state = {
cryptos:["bitcoin","ethereum","stellar","ripple"],
prices:[]
};
this.getPrice = this.getPrice.bind(this);
}
getPrice(){
const cryptos = this.state.cryptos;
console.log(cryptos);
for (var i = 0; i < cryptos.length; i++){
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
axios.get(cryptoUrl)
.then((response) => {
const data = response.data[0];
console.log(data.price_usd);
this.state.prices.push(data.price_usd);
console.log(this.state.prices);
})
.catch((error) => {
console.log(error);
});
}
}
If you want to receive the data in the order of the asynchronous calls you make, you can use Promise.all, that waits until all the promises of an array get executed and are resolved, returning the values in the order they were executed.
const cryptos = ['bitcoin', 'ethereum', 'stellar', 'ripple'];
const arr = [];
for (var i = 0; i < cryptos.length; i++){
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
arr.push(axios.get(cryptoUrl));
}
Promise.all(arr).then((response) =>
response.map(res => console.log(res.data[0].name, res.data[0].price_usd))
).catch((err) => console.log(err));
You could use a closure in the for loop to capture the value of i and use it as the index once the data is returned rather than using push:
getPrice(){
const cryptos = this.state.cryptos;
console.log(cryptos);
for (var i = 0; i < cryptos.length; i++) {
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
(function (x) {
axios.get(cryptoUrl)
.then((response) => {
const data = response.data[0];
console.log(data.price_usd);
var newPrices = this.state.prices;
newPrices[x] = data.price_usd;
this.setState({prices: newPrices});
console.log(this.state.prices);
})
.catch((error) => {
console.log(error);
});
})(i);
}
}

Categories