Array from Object is empty. Javascript and React - javascript

I am writing some react frontend application. I want to pass just header from table object to my function. I am using react-redux and redux-saga
In the reducer.js I am creating this object table, it has 2 arrays inside.
Code starts in render() and then calls Table which calls TableRows and TableHead
Later I pass table to TableHead as data variable (to avoid using as html table). But when I do data.header or data["header"] in TableHead method, the array I receive has 0 length and it is empty.
How can I get this array? I have to use map or some built-in functionality?
// Picture is created from this snippet
//AuthorsTable.js
const TableHead = ({data}) => {
console.log("Making header")
// var header = data.header
console.log(data)
console.log(data["header"])
console.log(data.header)
//reducer.js
...
case 'UPDATE_TABLE':
// console.log("Updating", action.author_obj.id)
var proto_obj = action.value;
// console.log("value:", obj)
var words = Object.keys(proto_obj).map(function (key) {
return {id: key, label: proto_obj[key]};
});
newState.table.header = [...newState.table.header, action.author_obj]
newState.table.content = [...newState.table.content, [words]]
return newState
...
//AuthorsTable.js
const TableHead = ({data}) => {
console.log("Making header")
// var header = data.header
console.log(data)
console.log(data["header"])
console.log(data.header)
var header = Object.keys(data.header).map(function (key) {
return {id: key, label: data[key]};
});
console.log(header)
const TableRows = ({data}) => {
return (<tbody><tr><td>content</td></tr></tbody>)
// for (let i = 0; i < 3; i++) {
// let children = []
// //Inner loop to create children
// for (let j = 0; j < 5; j++) {
// children.push(<td>{`Column ${j + 1}`}</td>)
// }
// return children
// }
}
const Table = ({data}) => {return(
<table>
<TableHead data={data} />
<TableRows data={data} />
</table>
)}
class AuthorsTable extends Component {
constructor(props) {
super(props);
}
render() {
const state = this.state;
// console.log(this.props.table)
return (
<Table data={this.props.table} />
// "Hello"
)
}
}
const mapStateToProps = state => {
return {
// authors: state.authors,
selectedItems: state.selectedItems,
table: state.table,
};
};
export default connect(
mapStateToProps,
)(AuthorsTable);

This was not working.
newState.table.header = [...newState.table.header, action.author_obj]
newState.table.content = [...newState.table.content, [words]]
Solution:
Creating new object, and then replacing it in state.
...
// reducer.js
// working code
const newState = { ... state };
var new_table = {
header: [...newState.table.header, action.author_obj],
content: [...newState.table.content, [words]],
}
newState.table = new_table
return newState
somebody knowing JS better can explain this further

Related

Tabulator does not re-sort data after adding/updating row

I am currently using "tabulator-tables" version 4.9.3 for a React application.
One of the requirements for this project is to receive live-stream data from a websocket which contains a mix of existing rows to be updated and new rows to be added.
In order to accomplish this, whenever I receive a new piece of data from the websocket, I check within componentDidUpdate() to see if this.props.data has changed and then use the updateOrAddData() function provided by tabulator to update the table accordingly.
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
this.table.updateOrAddData([data]);
}
}
As the table is populated with data coming from the websocket, I have noticed that Tabulator's embedded sorting algorithm is not called to determine the correct sorting position for the new data being added to the table by updateOrAddData(). If the user does not explicitly click a column header to re-sort this data, the data will be out of order based on the condition provided by the user.
What I have tried:
One possible solution that I tried was to utilize the returned promise from updateOrAddData() and call setSort() manually.
const { data } = this.props;
this.table.updateOrAddData(data).then(() => {
const sorters = this.table.getSorters();
this.table.setSort(sorters);
});
Unfortunately, whenever setSort() is called, the scrollbar returns to the top of the table, thus making it difficult to scroll throughout the table since new data is received approximately every second.
In order to demonstrate this problem, I have replaced the websocket connection with a setInterval() function which randomly generates data every second.
The code that I am currently using can be accessed at https://codesandbox.io/s/currying-brook-ck8r3 or below.
Code:
import React, { Component, createRef, useEffect, useState } from "react";
import Tabulator from "tabulator-tables";
import "../node_modules/tabulator-tables/dist/css/tabulator.min.css";
class TabulatorTable extends Component {
constructor(props) {
super(props);
this.el = createRef();
this.table = null;
}
componentDidMount() {
const { columns, options } = this.props;
this.table = new Tabulator(this.el, {
columns,
data: [],
...options
});
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
this.table.updateOrAddData([data]);
}
}
render() {
return <div ref={(el) => (this.el = el)} />;
}
}
const App = () => {
const [mockWsData, setMockWsData] = useState({});
const columns = [
{
field: "id",
title: "ID",
sorter: "number"
},
{
field: "letters",
title: "Random letters",
sorter: "string"
}
];
const options = {
height: "500px",
index: "id",
layout: "fitColumns",
reactiveData: true
};
useEffect(() => {
const generateFakeData = setInterval(() => {
setMockWsData({
id: Math.floor(Math.random() * Math.floor(15)),
letters: Math.random().toString(36).substring(2, 7)
});
}, 500);
return () => clearInterval(generateFakeData);
}, [setMockWsData]);
return (
<TabulatorTable columns={columns} data={mockWsData} options={options} />
);
};
export default App;
I decided to expand on the approach which utilized the promise returned by updateOrAddData() and set the scrollTop manually.
currentTopRow() {
const { rowManager } = this.table;
const { scrollTop } = rowManager.element;
let topRow = false;
let topOffset = false;
const rows = rowManager.getDisplayRows();
for (let i = rowManager.vDomTop; i <= rowManager.vDomBottom; i += 1) {
const row = rows[i];
if (row) {
const diff = scrollTop - row.getElement().offsetTop;
if (topOffset === false || Math.abs(diff) < topOffset) {
topOffset = diff;
topRow = row.getComponent().getIndex();
} else break;
}
}
return topRow;
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
const topRow = this.currentTopRow();
this.table.updateOrAddData([data]).then(() => {
this.table.setSort(this.table.getSorters());
if (topRow) {
this.table.scrollToRow(topRow, 'top', true);
}
});
}
}

New component overwrite the previous one

import React, { Component } from 'react'
import Item from './components/item'
import './App.css'
class App extends Component {
state = { item: "", array: [], check: false }
setItem = (event) => {
this.setState({
item: event.target.value
})
}
add = () => {
let item = this.state.item;
if (item != "") {
let arr = []
arr.push(item)
this.setState({ item: "", array: arr, check: true })
}
console.log(this.state.array)
}
render() {
return ( < div > < input type = "text"
value = { this.state.item }
onChange = { this.setItem }
/ > < button onClick = { this.add } > Add < /button > {
this.state.check ? < div > {
this.state.array.map(item => < Item name = { item }
/>) } < /div >: null
} < /div > );
}
}
export default App
I actually wrote this code for building a item buying remainder.The problem is first item added successfully but after that i can'nt add more item.Every time i tried it overwrite the previously added item.
In your add function, if there is no item in the state, your are declaring arr to be a new (empty) array, and only pushing one item to it. Then, you use setState to overrwrite the current array with your new one (Which only contains one item)
To add to the array, you would need to first copy all of the items currently in it, then push onto them
add = () => {
let item = this.state.item;
if (item != '') {
this.setState(prevState => {
let arr = [...prevState.array]; // Shallow copy of the array currently stored in the state
arr.push(item);
return { item: '', array: arr, check: true };
});
}
console.log(this.state.array);
};
You are overwriting your array every time you add a new item.
Try this inside the add function:
before
let arr = []
after
let arr = this.state.array

react native: what is the way to get only the `zoneData[index].Zone_ID` in the onPress button?

what is the way to get only the zoneData[index].Zone_ID in the onPress button ?
the below example take some data from table .
in this situation now while i press button so it return everything but i need only the zoneData[index].Zone_ID .
I have tried all kinds of ways but without success and would love to know how the right way.
import React from 'react';
import SQLite from 'react-native-sqlite-storage';
import {View, Button, ScrollView} from 'react-native';
const db = SQLite.openDatabase ({
name: 'Dogmim_DB',
createFromLocation: '~ / Dogmim_DB.db',
});
export default class SQLiteScreen extends React.Component {
constructor () {
super ();
SQLite.DEBUG = true;
}
SelectQuery () {
const promise = new Promise ((resolve, reject) => {
db.transaction ((tx) => {
tx.executeSql (
'SELECT Successor_Zones, Water_Source_Groups FROM tblUserConnectResponseData',
[],
(_, result) => {
for (let i = 0; i <result.rows.length; i ++) {
//// Successor_Zones
const Successor_Zones = result.rows.item (i) ['Successor_Zones'];
const zoneData = JSON.parse (Successor_Zones);
if (zoneData.length> 0) {
for (let index = 0; index <zoneData.length; index ++) {
console.log ('Zone_ID:' + zoneData [index] .Zone_ID);
console.log ('Zone_Name:' + zoneData [index] .Zone_Name);
}
}
//// Water_Source_Groups
const Water_Source_Groups = result.rows.item (i) [
'Water_Source_Groups'
];
const waterData = JSON.parse (Water_Source_Groups);
if (waterData.length> 0) {
for (let index = 0; index <waterData.length; index ++) {
console.log ('Group_Name:' + waterData [index] .Group_Name);
console.log ('Zone_ID:' + waterData [index] .Zone_ID);
}
}
}
resolve ({
isAny: true,
});
},
(_, err) => {
reject (err);
}
);
});
});
}
render () {
return (
<View>
<Button onPress = {this.SelectQuery} title = "Press Me" />
</View>
);
}
}

How to call a rest api and feed the result into a subsequent promise call

I have the following code that calls a rest api and then uses the resultant data and feeds the values into a subsequent api call. Not sure how to make this work..! You can see my comment in the second method and this displays the data, but because it's a promise I'm not sure how to pass that back?
Any ideas?
Thanks
Code snippet
componentDidMount() {
myMethod();
}
getBookings(id) {
getCustomerBookings(id).then(res => {
console.log(res); // displays the data correctly
return res;
});
}
myMethod() {
var self = this;
var myArray = [];
getCustomers().then(result => {
for(var index = 0; index < result.length; index++) {
myArray.push(<div className="col">
{result[index].customerId} // displays customer id as expected
{this.getBookings(result[index].customerId)} // attempt
</div>
self.setState({customers: myArray});
});
}
You could split this the single component into two - CustomerContainer and Customer, and tie the second API call to the mounting of the Customer component.
import React, { Component } from 'react';
class CustomerContainer extends Component {
constructor() {
super();
this.state = {
customers = []
}
}
async getCustomers() {
// fetch customers
}
async componentDidMount() {
customers = await this.getCustomers();
this.setState({ customers })
}
render() {
const { customers } = this.state
return (
{customers.length > 0 && customers.map(customer => {
return <Customer customerId= {customer.id} />
})}
)
}
}
class Customer extends Component {
constructor() {
super();
this.state = {
bookings = [];
}
}
async getBookings(id) {
// fetch booking
}
async componentDidMount() {
bookings = await this.getBookings(this.props.customerId);
this.setState({ bookings })
}
render() {
const { bookings } = this.state;
return (
<div className="col">
{this.props.customerId} // displays customer id as expected
</div>
)
}
}
As you are doing with getCustomers(), you have to get the result of the promise with then. So your code would look like this:
myMethod() {
var self = this;
var myArray = [];
getCustomers().then(result => {
for(var index = 0; index < result.length; index++) {
this.getBookings(result[index].customerId).then(bookings => {
myArray.push(<div className="col">
{result[index].customerId}
{bookings}
</div>);
});
}
self.setState({customers: myArray});
});
}
Note that this solution assumes you are not using ES6 async/await structure. Otherwise the other answers are better.
How about this
async myMethod() {
var self = this;
var myArray = [];
var result = await getCustomers();
for(var index = 0; index < result.length; index++) {
var booking = await this.getBookings(result[index].customerId);
myArray.push(<div className="col">
{result[index].customerId}
{booking}
</div>
}
self.setState({customers: myArray});
}

map dynamic array in react from a smart contract with values an address and a string

I am trying to map a dynamic array with an address and a string from solidity to react to look like a table or print out the address and string in a list but I can't manage to separate those 2 values.
class App extends Component {
constructor(props) {
super(props);
this.state = {
Data: []
};
}
GetData = async () => {
const { accounts, contract, count, Data } = this.state;
const data = [];
for (var i = 0; i < count.length; i++) {
const dataa = await contract.methods.getInfo(i).call();
data.push(dataa);
}
console.log(data);
this.setState({ Data: JSON.stringify(data) });
};
render() {
return (
<>
<button onClick={this.GetData}>Show</button>
<h1>{this.state.Data}</h1>
</>
);
}
}
export default App;
This is what my console prints and it shows the data in the website as 0:<address> 1: <string>
(2) [Result, Result]
0: Result
0: "0x7e3ce0fc8F95Bb83A4f5131912DacBFf11B9d4f8"
1: "{test1}"
__proto__: Object
1: Result {0: "0x514bdB4F417926027dDa4f0ccb2a6674a31D4BcB", 1: "{test2"}
length: 2
__proto__: Array(0)
Try separating before you stringify. Actually, why do you stringify at all?
This should do:
class App extends Component {
constructor(props) {
super(props);
this.state = {
Data: []
};
}
GetData = async () => {
const { accounts, contract, count, Data } = this.state;
const data = [];
for (var i = 0; i < count.length; i++) {
const dataa = await contract.methods.getInfo(i).call();
data.push(dataa);
}
console.log(data);
this.setState({ Data: data });
};
render() {
return (
<>
<button onClick={this.GetData}>Show</button>
{ this.state.Data.map(item => <h1>{item.toString()}</h1>) }
</>
);
}
}
export default App;

Categories