React error during button click and adding data to database - javascript

I'm trying to add data into a database and display it in a table on my front-end at the same time. Adding the data into the database is done by a button click.
The data is added to the database without any problems but when trying to display it in the table I just get some errors.The error picture is here.
The table I'm using looks like this (react bootsrap datatable)
<MDBDataTable
striped
small
data={data}
tbodyTextWhite
theadTextWhite
noBottomColumns
/>
The data variable which is an array looks like this:
const data = {
columns: [
//RANDOM COLUMNS HERE
],
rows: tableRowsData(history_numbers_user_database),
};
Example of output for rows:
{
id: "4",
nr: "50898980",
service: "ps5",
id3: "500",
delete: "yes",
},
{
id: "5",
nr: "50898980",
service: "tv",
id3: "500",
delete: "yes",
}
The tableRowsData(history_numbers_user_database) looks like this
const tableRowsData = (database_data) => {
if (!database_data) {
return [];
}
return database_data;
};
and the history_numbers_user_database looks like this:
const history_numbers_user_database = useSelector((state) => {
console.log(state);
return state.auth.user ? state.auth.user.history_phone_numbers : "";
});
On the button click data needs to be added to the history_numbers_user_database (a new row needs to be added to the table as well) in the database and it should be displayed dynamically in the front-end.
The data variable should always be an array and I'm guessing that's why I'm facing this issue but I don't understand how the button click which change the variable type or if it's something related to re-rendering.

const tableRowsData = (database_data) => {
if (!database_data) {
return [];
}
return database_data;
};
You are calling this function with a string, and in it's second case it just returns what you passed into it.
In your history_numbers_user_database selector, it returns an empty string at the end of the ternary.
So when you call tableRowsData(""), it returns a string, which is not an array, hence .filter is not a function.

Related

How to use spread operator in setstate react class component

I am developing a component where I will get the data from a call back function. Initially the state of the component will be empty [], later once the callback function is called I need to update the values into the state. At a time I'll recive only one array, meaning user can add one item at a time that item will consists of nested objects and array values. I have added the logic for the same to handle the scenario, but when I am testing in jest when I am trying to add another set of item from mock meaning the user can select next item when the done with selecting and submitting the first item at that time my logic is getting failed, I am not getting where I went wrong, could any one help me to resolve this issue, thanks in advance! I have added the mock data structure and logic and jest test below.
Mock:
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
}
ItemID:'123'
}
Code:
class ItemComp extends React.Component{
this.state = {
processingItems:[]
onAddItemHandle = (processingItem) => {
this.setState(prevState => ({
processingItems: [...prevState.processingItems, processingItem]
}))
}
JEST:
describe('handleonAddItem', () => {
it('should allow to add multiple items based on prevState', () => {
const compView = mountWithIntl(
<compView
itemId={12}
/>
}
const instance = compView.find(compViewComponent).instance();
instance.onAddItemHandle(items) // when I am giving only one instance my logic is working
instance.onAddItemHandle(items) //when I am giving it for second time it's failing I am getting error like expected - 0 , received +18 I want to update the items here when user clicks for second time but it is failing.
expect(instance.state.processingItems).toEqual([items])
Missing a ',' before the ItemID is the only issue I faced while reproducing.- https://codesandbox.io/s/intelligent-chaplygin-0ot56e?file=/src/App.js
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
},
ItemID:'123'
}

How does fetchMore return data to the component?

I am trying to follow the example of cursor-based paginating with React Apollo (https://www.apollographql.com/docs/react/data/pagination/#cursor-based) but am struggling with how my component that rendered the original data gets the new (appended) data.
This is how we get the original data and pass it to the component:
const { data: { comments, cursor }, loading, fetchMore } = useQuery(
MORE_COMMENTS_QUERY
);
<Comments
entries={comments || []}
onLoadMore={...}
/>
What I'm unsure of is how the fetchMore function works.
onLoadMore={() =>
fetchMore({
query: MORE_COMMENTS_QUERY,
variables: { cursor: cursor },
updateQuery: (previousResult, { fetchMoreResult }) => {
const previousEntry = previousResult.entry;
const newComments = fetchMoreResult.moreComments.comments;
const newCursor = fetchMoreResult.moreComments.cursor;
return {
// By returning `cursor` here, we update the `fetchMore` function
// to the new cursor.
cursor: newCursor,
entry: {
// Put the new comments in the front of the list
comments: [...newComments, ...previousEntry.comments]
},
__typename: previousEntry.__typename
};
}
})
}
From what I understand, yes, once my component will cal this onLoadMore function (using a button's onClick for example), it will fetch the data based on a new cursor.
My question is this. I'm sorry if this is too simple and I'm not understanding something basic.
How does the component get the new data?
I know the data is there, because I console logged the newComments (in my case, it wasn't newComments, but you get the idea.) And I saw the new data! But those new comments, how are they returned to the component that needs the data? And if I click the button again, it is still stuck on the same cursor as before.
What am I missing here?
In the updateQuery function lets you modify (override) the result for the current query. At the same time your component is subscribed to the query and will get the new result. Let's play this through:
Your component is rendered for the first time, component will subscribe to the query and receive the current result of the query from the cache if there is any. If not the query starts fetching from the GraphQL server and your component gets notified about the loading state.
If the query was fetched your component will get the data once the result came in. It now shows the first x results. In the cache an entry for your query field is created. This might look something like this:
{
"Query": {
"cursor": "cursor1",
"entry": { "comments": [{ ... }, { ... }] }
}
}
// normalised
{
"Query": {
"cursor": "cursor1",
"entry": Ref("Entry:1"),
}
"Entry:1": {
comments: [Ref("Comment:1"), Ref("Comment:2")],
},
"Comment:1": { ... },
"Comment:2": { ... }
}
User clicks on load more and your query is fetched again but with the cursor value. The cursor tells the API from which entry it should start returning values. In our example after Comment with id 2.
Query result comes in and you use the updateQuery function to manually update the result of the query in the cache. The idea here is that we want to merge the old result (list) with the new result list. We already fetched 2 comments and now we want to add the two new comments. You have to return a result that is the combined result from two queries. For this we need to update the cursor value (so that we can click "load more" again and also concat the lists of comments. The value is written to the cache and our normalised cache now looks like this:
{
"Query": {
"cursor": "cursor2",
"entry": { "comments": [{ ... }, { ... }, { ... }, { ... }] }
}
}
// normalised
{
"Query": {
"cursor": "cursor2",
"entry": Ref("Entry:1"),
}
"Entry:1": {
comments: [Ref("Comment:1"), Ref("Comment:2"), Ref("Comment:3"), Ref("Comment:4")],
},
"Comment:1": { ... },
"Comment:2": { ... },
"Comment:3": { ... },
"Comment:4": { ... }
}
Since your component is subscribed to the query it will get rerendered with the new query result from the cache! The data is displayed in the UI because we merged the query so that the component gets new data just as if the result had all four comments in the first place.
It depends on how you handle the offset. I'll try to simplify an example for you.
This is a simplified component that I use successfully:
const PlayerStats = () => {
const { data, loading, fetchMore } = useQuery(CUMULATIVE_STATS, {
variables: sortVars,
})
const players = data.GetCumulativeStats
const loadMore = () => {
fetchMore({
variables: { offset: players.length },
updateQuery: (prevResult, { fetchMoreResult }) => {
if (!fetchMoreResult) return prevResult
return {
...prevResult,
GetCumulativeStats: [
...prevResult.GetCumulativeStats,
...fetchMoreResult.GetCumulativeStats,
],
}
},
})
}
My CUMULATIVE_STATS query returns 50 rows by default. I pass the length of that result array to my fetchMore query as offset. So when I execute CUMULATIVE_STATS with fetchMore, the variables of the query are both sortVars and offset.
My resolver in the backend handles the offset so that if it is, for example, 50, it ignores the first 50 results of the query and returns the next 50 from there (ie. rows 51-100).
Then in the updateQuery I have two objects available: prevResult and fetchMoreResult. At this point I just combine them using spread operator. If no new results are returned, I return the previous results.
When I have fetched more once, the results of players.length becomes 100 instead of 50. And that is my new offset and new data will be queried the next time I call fetchMore.

Trying to set a value by referencing separate json

Im trying to separate out the functionality of my model and the data so ive created a separate json file with a basic table
when my model builds it creates an object and i need it to create a value in it based on a value coming in:
{
"1":"apple",
"2":"banana",
"3":"orange",
"4":"grape"
}
async save (xmlOrder) {
let customerOrder = {
ID: xmlOrder.ID,
Name: xmlOrder.Name ,
ItemCode: xmlOrder.ItemCode ,
Fruit: (This set by referencing the json, based on the Item code coming in above)enter code here
}
You can import that json object in file where you're having your model, than based on input to function you can get value out of object.
let obj = {"1":"apple","2":"banana","3":"orange","4":"grape"}
function save (xmlOrder) {
let customerOrder = {
ID: xmlOrder.ID,
Name: xmlOrder.Name ,
ItemCode: xmlOrder.ItemCode ,
Fruit: obj[xmlOrder.ItemCode] || 'Not in list',
}
return customerOrder
}
console.log(save({ID:33,Name:'Name',ItemCode:'2'}))
console.log(save({ID:303,Name:'Name1',ItemCode:'21'}))

Dynamically passing JSON value to another template

I have one template, let's call it Template A that prints JSON data into a table, one column includes a button which is conditionally rendered when has_violations equals true.
An example of the table:
Table
What I want to accomplish is to take the driver_id that is associated with that particular row into the router link and have it passed onto a different template file let's call it Template B.
But how can I accomplish this using Vuex Store?
Sample JSON data:
{"driver_id":1,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"13","driver_trailer":"83","driver_status":"driving","has_violations":false},
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
{"driver_id":3,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"80","driver_trailer":"27","driver_status":"driving","has_violations":true},
Basic steps:
Get index of row on button click.
Get index of JSON data using value from Step 1.
Store the JSON data from Step 2 into Vuex.
Send user to Template B using router.
Retrieve data from Store when in Template B
Because you did not show your exact structure, the code below is just a basic structure.
Here's the code:
/* VUEX demo */
new Vuex.Store({
state: {
driver_data: undefined
},
mutations: {
recordDriver({ state }, payload){
state.driver_data = payload;
}
}
});
/* TEMPLATE A demo */
new Vue.component('template-a', {
data: function(){
return {
// Assume this is the JSON
driverJSON: [
{ driver_id: 1, driver_name: 'John Smith' },
{ driver_id: 2, driver_name: 'Bob John' }
]
};
},
methods: {
onButtonClicked: function(e){
const button = e.target;
const td = button.parentElement;
const tr = td.parentElement;
const indexOfTr = [...tr.parentElement.children].findIndex(row => row === tr);
const dataToStore = this.driverJSON[indexOfTr];
// Store data into $store
this.$store.commit('recordDriver', dataToStore);
// After storing, direct page using $router
this.$router.go({ ... });
}
}
});
/* TEMPLATE B demo */
new Vue.component('template-b', {
data: function(){
return {
// Get driver data using $store
driver: this.$store.state.driver_data
}
}
});
I like Yong's answer, but I would rather suggest you to pass the driverID as a prop to your route and then use a VueX getter to get the violations for the particular ID.

Javascript. adding items to an array updates all items

This question is somewhat related to this issue I had earlier today:
Adding items to an array in javascript
It works to add items to my array now, but it seems that when I update the array all items will be the same even though the object passed into the method is different everytime
My method looks like this:
addShoe(shoe) {
console.log("Adding new shoe to collection: ");
console.log(shoe);
this.setState(
{
shoes: [...this.state.shoes, shoe]
},
function() {
console.log("Shoe collection:");
console.log(this.state.shoes);
}
);
}
So after one run, this is what the console in Chrome looks like. Which seems to be right:
When I try to add one more to the collection, this is what happens:
Now my collection contains two items which is correct, but it seems like all items in the collection has the same data?
What am I doing wrong here?
EDIT
In another React component I have the following state:
this.state = {
shoe: {
selectedBrand: "",
selectedEU: "",
selectedUS: "",
selectedUK: "",
selectedFraction: ""
}
};
Once a field is updated with a new value, the following method will be triggered:
updateSelectedValues(property, event) {
const shoe = this.state.shoe;
shoe[property] = event.value;
this.setState({ shoe: shoe });
}
When a button in this modal window is closed, the this.state.shoe will be pass as a param to method in the "parent" component.

Categories