Displaying Node MySQL Results in React Using State - mysql

My node.js MySQL query returns a single row wrapped in [RowPacketData] which I can normally access the ID field using results[0].ID.
However, when I store the result in React state (using hooks) it does not work. I can access the result object, but not fields within it.
function MyReactComponent() {
const [dbEntry, setDbEntry] = useState();
useEffect(() => {
const fetchData = async () => {
const result = await queryFunc(`SELECT * FROM table LIMIT 1`);
console.log(result[0]); // <-- Works (shows [RowDataPacket] object)
console.log(result[0].ID); // <-- Works (shows ID)
setDbEntry(result);
};
fetchData();
}, []);
console.log(dbEntry[0]); // <-- Works (shows [RowDataPacket] object)
console.log(dbEntry[0].ID); // <-- TypeError: Cannot read property '0' of undefined
return (
<p>
{dbEntry[0].ID} // <-- How do I render here?
</p>
)
}
What's going on here? I have a feeling React is coercing the result object somehow, but I can't figure it out...

When you need to display data that comes from an async font(API calls for example), it's possible (actually almost certain) that it won't be available by the time the first render occurs, to solve that there is actually a few things you could do:
Placeholder state
You could have a model of what the data will look like described as your initial state, so properties won't be undefined anymore:
const [state, setState] = useState({
data:[
{name: ''}
]
})
Assuming that your data will have this format accessing state.data[0].name won't throw an error. This could be useful in some cases but I personally don't like the approach.
Conditional Render
At each render you should check for a condition and only if satisfied render the piece of code:
return(
<>
<div>Title</div>
{Boolean(state.data.length) && <div>{state.data[0].name}</div>}
</>
)
Suspense
That one is brand new, if you have a component tha't need to perform side effects before render it's content, you should have a fallback content to be displayed while the async action is being perform.
<Suspense fallback={<span>Loading</span>}>
<MYAsyncComponent />
</Suspense>

Related

Apparently no causes for the error "TypeError: Cannot read properties of undefined "

I have to show in the layout some data I take from a database. Here is the code I use to fetch the data:
const [array, setArray] = useState([])
const collectionRef = collection(db, "Collection")
const q = query(collectionRef, orderBy("Order", "desc"))
useEffect(() => {
auth.onAuthStateChanged(function (user) {
if (user) {
onSnapshot(q, (data) =>
setArray(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })))
);
}
})
}, [auth]);
Below is the html
<div>
<h2>{array[0].Name}</h2>
</div>
What baffles me most is that the error does not always show up, sometimes everything works and sometimes it stops working without apparent causes. If I do console.log of the array sometimes it prints the data and everything works, sometimes it prints an empty array and does nothing.
I state that that array is never modified, it is loaded at the beginning of the code and the data is only read.
array is empty for the first render and for all the other renders until the data is returned. Try something like:
<div>
<h2>{array.length && array[0].Name}</h2>
</div>

How to initialize checkbox based on dynamic value in React

I am trying to initialize a checkbox to either checked or unchecked based on a value in local storage (chrome extension). I was trying to trigger a function on load that checks the a value in local storage and returns a Boolean value. Since there are multiple checkboxes I need to pass the element id to the function as well.
Here is a semi-pseudo code version of what I was thinking :
const getChecked = (el: any): boolean => {
val = el.id from local storage;
return val;
}
const App = () => (
<div className="App">
<input
type="checkbox"
id="checkboxID"
defaultChecked={getChecked}
/>
</div>
);
But something like this doesn't work because defaultChecked={getChecked} gives me the error " Type '(el: any) => boolean' is not assignable to type 'boolean | undefined'."
Any ideas on what to do?
Thanks.
Please try using checked parameter, instead of defaultChecked.
To get the value from local storage I would suggest you to use a function like this:
const getChecked = (el:any) => {
return JSON.parse(localStorage.getItem("el.id"))
};
Make sure that el.id value is true or false in local storage.

How to change contents of a column in each row of a table without using a props in Vue.js (Element-ui)?

I am stuck on this problem from quiet sometime. I have created a table with 3 columns out of which the for two columns I can use prop property to display the contents of each row for those two columns. Now, for the third column, I want to display contents from another array, how do I display this information in the third column.
Please find the example of the code below:
This is the HTML Code
<el-table :data = "data_table">
<el-table-column
prop = "Id"
label= "Unique ID">
</el-table-column>
<el-table-column
prop = "color"
label = "Color">
</el-table-column>
<el-table-column
prop = ""
label = "Count">
<template slot-scope="scope">
<el-button type="text" #click="dialogVisible = true; get_total_count_users(scope.row)">{{users.length}}</el-button>
<!--Skipping the code for dialog box..I have tested that part pretty well, so I know it works. -->
</template>
</el-table-column>
</el-table>
This is the javascript part of the code:
<script>
export default {
data(){
return{
data_table:[], // I fill the data_table array using some AJAX/Axios calls which has props Id and Color.
users: [], // I fill the users array using some AJAX/Axios calls which has multiple user information.
};
},
methods:{
get_total_count_users(row){
// axios call, etc. This part works successfully, I checked the values.
}
}
</script>
A little explanation for the above code:
I make an AJAX/Axios call to an API which return me a list/array of value in data-table array. It had two props in it, that is Id and Color. I make another axios/AJX call to an api which returns me the list of the users based on the Id present on that row of the table. Each row will have a unique Id. Using that Id, I make an axios call to an api .. example, www.example/{{Id}}.com .This returns me a list of users linked to that id. Now, my next task is to display the total users (by taking the length of the users array) and displaying it as a button. But, as I am not using prop to display the values (length of users array for each row), the value in the button is displayed the same through all the rows. It keeps changing for the entire column if I click a button on any of the rows.
get_total_count_users(scope.row) function is used to make an axios/AJAX call to www.example/{{Id}}.com and stores multiple user information tied with that Id in users array.
Please refer to the image below:
Initially, all values are 0, as the Id in the first row has 0 users attached to it.
When I click on the 3rd rows button (which initially has 0 in it), all the values in that column change to 2, as the Id in the 3rd row has two users tied to it.
Hence, the issue here is that, I simply want to display total number of users (count) each row based on that id without using the prop property.
Hope the above explanation and example is helpful to understand the problem.
Any help would be appreciated.
The answer will solve the problem but is not a specific solution that is defined to get data from multiple arrays into your data table.
You can chain your axios responses and then use a foreach to add the variables to your object inside array.
So I am assuming that you are calling a method on mount like this:
data: () => ({ tableData: [] }),
mounted() {
this.getData();
}
now within your getData() function make different axios calls and put your response into the table, with something like this.
methods: {
getData() {
var myTableData = [];
axios({ method: 'GET', url: 'http://first-url', headers: [...] }).then(res => {
//we have got the table data here
myTableData = res.data;
//looping through data and making axios calls based on data.
myTableData.foreach((row, index) => {
var newUrl = 'https://getdatafrom.com/'+ row.id+'/'; //using the data from previous call.
axios({ method: 'GET', url: newUrl, headers: [...]}).then(res2 => {
var counts = res.data;
// adding variable myCount to the table data array.
row.myCount = counts[index];
}).catch(err => { console.error(err)})
})
// setting data object
this.tableData = myTableData;
}).catch(err => { console.error(err) })
}
}
let me know if you have any issue.

RxJs Interval with takeUntil to publish last value

I have some code which polls until a task is complete
See below
this.simulationStatus =
interval(2000).pipe(
switchMap(
() => from(this.simulationService.getSimulationStatus(this.route.snapshot.paramMap.get('jobId')))),
takeUntil(this.stopPoll),
tap(simulation => {
if (simulation && simulation.complete) {
if (this.stopCount == 1) {
// Get once after complete
this.stopPoll.next(true);
}
this.stopCount++;
}
})
);
I have tried using takeUntil and takeWhile the problem is that that the last value is never published once the task is complete.
To get around this I have to include the tap method to with the stopPoll subject and incrementing the stopCount to get the last value.
So the above works but just feels a bit messy, I'm sure there must be a better way of achieving this?
I would have expected takeUntil to publish the last value or have an override to tell it to e.g takeUntil(observable, {publishLast: true})
BTW Update, the observable is subscribed to by an Angular 6 template
Thanks in advance
One thing you can do is use a custom takeWhile-like operator like this:
const completeWith = <T>(predicate: (arg: T) => boolean) => (
source: Observable<T>,
) =>
new Observable<T>(observer =>
source.subscribe(
value => {
observer.next(value);
if (predicate(value)) {
observer.complete();
}
},
error => observer.error(error),
() => observer.complete(),
),
);
It doesn't seem like a good idea to see it as a variation of takeWhite because it's not just taking values while a condition holds, but also emits an extra value.
It might be that a more elegant solution would be make the simulation status observable emit two kinds of values: next notifications and completion notifications, similarly to how materialize/dematerialize operators work.
This has in the meantime been implemented in rxjs as takeWhile(condition, ?inclusive):
timer(0, 10).pipe(
takeWhile((x) => x < 3, true)
)
emits 0, 1, 2, 3
You can also create subject and emit using next() if you want to complete the observable.
this.stopPoll: Subject<any> = new Subject<any>();
If you want to do complete the subscription. you can call this.stopPoll.next(true);
you can access the data in subscribe()
this.simulationStatus.subscribe(success=>{}, failure=>{}, complete=>{});

Declare a variable and store retrieve data inside

I have managed to retrieve data from Firebase, however, I am unable to store the data into a variable I have declared. I want to store in a variable is so that I am able to use at another method.
Please help.
These are the codes that I have tried.
1st method, rest api
retrieveUser(){
this.restProvider.retrieveUser(this.emailAdd, this.pw)
.subscribe(listUser => {
this.users = userList.results //trying to store to users variable
console.log(listUser);
},
err => {
console.log(err);
},
() => console.log('success')
);}
2nd method to use for some IF-ELSE
loginBtnPress(event){
this.retrieveUser();
console.log(this.users);
}
You are doing it wrong. this.users is getting updated using observables. So, if you try to retrieve its value before the value has been updated, you wont get it as expected. You can call it as below,
retrieveUser(){
this.restProvider.retrieveUser(this.emailAdd, this.pw)
.subscribe(listUser => {
this.users = userList.results //trying to store to users variable
callYourMethodWithUpdatedValue(this.users); // <-- Pass value here
},
err => {
console.log(err);
},
() => console.log('success')
);}
The callYourMethodWithUpdatedValue is called every time(and as soon as) the value this.users is updated
Update 1
You can't retrieve async value in one method and expect that value to be used in some other method on button click. Reason being, your are expecting value on that button click , but since its a async call (can take n secs), you can't be assured that the value will be populated when the value is used through some button call.
That's wrong implementation. You need to change the architecture to either of below:
Don't route to that page unless the user value has been resolved (received). Check canActivate and canActivateChildren.
Change the UX where you are trying to retrieve the use async variable on button click, and activate the button when the value has been received (Here you can use Subject ). Something like this demo