How do I loop and iterate a row in React application - javascript

I am learning programming and have an issue. I would like to iterate and loop (i think are the terms) a row. The row will contain 3 columns who's data comes from a REST api. Once the row has 3 columns fulled, I would like a new row to be created with 3 more columns.
What is the easiest way to achieve this thanks.
My code is here https://jsfiddle.net/Ory4n/8k7dabyr/1/ and I found a possible solution code here https://jsfiddle.net/dya52m8y/2/ however I do not know how to implement that code into my own project as of yet.
class Test extends Component {
constructor(){
super();
this.state = {
pages: []
}
}
componentDidMount() {
let pagesURL = "http://creartem.nz/wp-json/wp/v2/projects";
fetch(pagesURL)
.then(response => response.json())
.then(response => {
this.setState({
pages: response
})
})
}
render() {
let pages = this.state.pages.map((page, index) => {
return (
{/* Start Loop */}
<div key={index}>
<h2>{page.title.rendered}</h2>
<p>{page.acf.technologies}</p>
<p>{page.acf.team}</p>
</div>
{/* end Loop */}
)
})
return (
<section>
{pages}
</section>
);
}
}
export default Test;

If you want to render an array of components, you must call a function that in turn returns a call to the .map() method you had specified. See below:
render() {
const renderPages = () => {
return this.state.pages.map((page, index) => {
return (
<div key={index}>
<h2>{page.title.rendered}</h2>
<p>{page.acf.technologies}</p>
<p>{page.acf.team}</p>
</div>
);
})
}
return (
<section>
{renderPages()}
</section>
);
}

I could be wrong, but if I understand correctly, you would like to create a new row containing a h2 and two p tags. You could try using a table to achieve this output. Something like:
const renderPages = () => {
return (
<div>
<table>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
{this.state.pages.map(page => {
return (
<tr key={page.id}>
<td><h2>{page.title.rendered}</h2></td>
<td><p>{page.acf.technologies}</p></td>
<td><p>{page.acf.team}</p></td>
</tr>
)
})}
</table>
</div>
)
}
Also, avoid using index as your key. It causes weird issues. Use something like an id.
Here is a similar example on codepen: https://codepen.io/mkempinsky/pen/gBapqg?editors=1111

Related

My function does not render the data I supply to it. What do I do?

I am trying to write a table that should be writing out properties of an object in React. For some unknown reason I cannot get it to produce any output.
const renderItem = (item) => {
return (
<div>
{Object.entries(item).map(([key, value]) =>
{console.log(value)}
(
*variations in next segment
)
</div>
*in this line I tried:
{value} // with instead of wrapping elements as shown above
{value}
Test // wrapped in elements
The console.log(value) returns all the values it should so I know I supply the data correctly and it is iterating through the array as it should but somehow I cannot get it to produce any output inside of ().
I tried playing with many different variations, thought the problem might be in css but that is also not the case. I tried outputing data also simply in or , I tried moving the function to a whole new file, but nothing.
EDIT. Here is more info:
My main App.js:
import './App.css';
import HierarchyTable from './components/HierarchyTable.js';
import exampleData from './data/example.json';
function App() {
return (
<div className="App">
<HierarchyTable datafed={exampleData} ></HierarchyTable>
</div>
);
}
export default App;
My HierarchyTable.js
import React, { useState } from 'react';
import '../App.css';
export default function HierarchyTable({datafed}) {
const [data, setData] = useState(datafed)
const renderTable = (item) => {
return(
Object.entries(item).map(([key, value]) => {
if (key !== "children"){
renderItem(value)
}
else {
renderTable(value)
}
}
)
)
};
function renderItem (item) {
return Object.entries(item).map(([key, value]) => {
return (<tr><td key={key}>{value}</td></tr>)
}
)}
return (
<div>
<table>
<thead>
<tr>
{Object.entries(data[0].data).map(([key, value]) => (
<th
// className = "HeaderItem"
>{key}</th>
)
)}
</tr>
</thead>
<tbody>
{data.map((item) => renderTable(item))}
</tbody>
<tfoot>
</tfoot>
</table>
</div>
);
}
I tried doing it in many different variatons. The renderItem function gets the data I need as I can console.log it but it does not output the html elements as it should. It breaks my spirit.
You need to properly return something from the map function:
const renderItem = (item) => {
return (
<div>
{Object.entries(item).map(([key, value]) => {
console.log(value)
return value
//or return (<p>{value}</p>)
})}
</div>
)
You most likely confused the shorthand map function that looks like data.map(value => (value))
If you use brackets {}, you need to specifically return something with "return". If you use (value) only, it is short from {return value}

Build new array from existing array based on number of component calls

Backstory
Note: This question is an expansion to the answer T.J. Crowder provided here.
I expanded on this by creating a state to hold the rows array so that I could update the array (remove from) using setState.
I added a handleClick function to handle what I would like the user to be able to do to the array based on the index of the element being clicked. (currently all that is included is that right click removes the target index from the array.
/rowList.js
import React, { Component } from "react";
import Row0 from "./../rows/row0";
import Row1 from "./../rows/row1";
import Row2 from "./../rows/row2";
import Row3 from "./../rows/row3";
const row0 = () => <Row0 />;
const row1 = () => <Row1 />;
const row2 = () => <Row2 />;
const row3 = () => <Row3 />;
class RowInfo {
static id = 0;
constructor(Comp) {
this.Comp = Comp;
this.id = RowInfo.id++;
}
}
class RowList extends Component {
constructor() {
super();
this.state = {
rows: [
new RowInfo(row0),
new RowInfo(row1),
new RowInfo(row2),
new RowInfo(row3)
]
};
}
handleClick = (a, b) => e => {
e.preventDefault();
if (e.nativeEvent.which === 1) {
//left click
console.log(a); //for testing
console.log(b); //for testing
} else if (e.nativeEvent.which === 3) {
//right click
this.setState({
rows: this.state.rows.filter((_, i) => i !== a)
});
}
};
render() {
return (
<>
{this.state.rows.map(({ id, Comp }) => (
<tr
key={id}
onClick={this.handleClick(id)}
onContextMenu={this.handleClick(id)}
>
<Comp />
</tr>
))}
</>
);
}
}
export default RowList;
I then tested calling the <RowList /> component twice so that I can test removing rows across two components.
Note: Mod0 is imported into a /main.js file which is imported to /index.js which is rendered to <div id="root"></div> in index.html
/mod0.js
import React, { Component } from "react";
import RowList from "./../rows/rowList";
class Mod0 extends Component {
render() {
return (
<>
<table>
<tbody>
<RowList />
</tbody>
</table>
<table>
<tbody>
<RowList />
</tbody>
</table>
</>
);
}
}
Problem
When testing the removal I realised a crucial error, the nature of which my current knowledge can only make me relatively certain of so my explanation may be innacurate/flawed. It would seem I can only remove from the first 4 rows that are rendered in <RowList /> as the second 4 rows do not corrospond to the array in this.state.rows.
I think the solution is to build a new array based on the original state array and the amount of times <RowList /> is called. And render that array instead.
Perhaps I could update the state could look something like this to begin with:
constructor() {
super();
this.state = {
rows: [
new RowInfo(row0),
new RowInfo(row1),
new RowInfo(row2),
new RowInfo(row3)
],
rendRows: this.rendRowsTemp
};
this.rendRowsTemp = []; //push to build a new array into here?
}
And then use the new array instead like so:
render() {
return (
<>
{this.state.newRows.map(({ id, Comp }) => (
<tr
key={id}
onClick={this.handleClick(id)}
onContextMenu={this.handleClick(id)}
>
<Comp />
</tr>
))}
</>
);
}
Expected Result
I need a method to build the new array based on the original state array and the amount of times <RowList /> is called.
I think the problem is that in the filter's condition (this.state.rows.filter((_, i) => i !== a)) you're using the RowInfo's index in the array not it's ID, try: this.state.rows.filter(({ id }) => id !== a)

ReactJS - reading json data

I am trying to populate table data by importing json. However, getting below error:
Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys { list }). If you meant to render a collection of children, use an array instead.
Below is the code I am using:
import reg from "./data/reg.json";
class TableRow extends Component {
render() {
const { data } = this.props;
const list = data.map(adata => {
return (
<tr>
<React.Fragment>
<td key={adata.FName}>{adata.FName}</td>
<td key={adata.LName}>{adata.LName}</td>
<td key={adata.Age}>{adata.Age}</td>
</React.Fragment>
</tr>
)//return
})//regionslist
return (
{ list }
);//return
} //render
} //class
class DTable extends Component {
render() {
return (
<Table striped bordered hover>
<TableHeader />
<tbody>
<TableRow data={this.props.data} />
</tbody>
</Table>
);
}
}
class DataTable extends Component {
render() {
return (
<DTable data={reg} />
);//return
} //render
}
Problem
I recreated your setup trying to reproduce your error. You can see what I reproduced here: Stackblitz. The issue seems to lie in the fact that your map creates some table rows, but there is no parent element to wrap them, which is required by JSX.
Solution
This can be fixed by wrapping {list} inside of a React.Fragment:
class TableRow extends Component {
render() {
const { data } = this.props;
const list = data.map(adata => {
return (
<tr>
<td key={adata.FName}>{adata.FName}</td>
<td key={adata.LName}>{adata.LName}</td>
<td key={adata.Age}>{adata.Age}</td>
</tr>
)//return
})//regionslist
return (
<React.Fragment>
{ list }
</React.Fragment>
);//return
} //render
} //class
Adding the React.Fragment around the list items resolved the issue. You can see the working solution here: Stackblitz
Alternatives
As of React 16.2 (Read more) you also have the option to return empty JSX tags instead of typing out React.Fragment. The solution above would look like this:
class TableRow extends Component {
render() {
const { data } = this.props;
const list = data.map(adata => {
return (
<tr>
<td key={adata.FName}>{adata.FName}</td>
<td key={adata.LName}>{adata.LName}</td>
<td key={adata.Age}>{adata.Age}</td>
</tr>
)//return
})//regionslist
return (
<>
{ list }
</>
);//return
} //render
} //class
Lastly it is also possible to return an array of elements since React 16.0. If you would choose to do this the above code would look like this then:
class TableRow extends Component {
render() {
const { data } = this.props;
const list = data.map(adata => {
return (
<tr>
<td key={adata.FName}>{adata.FName}</td>
<td key={adata.LName}>{adata.LName}</td>
<td key={adata.Age}>{adata.Age}</td>
</tr>
)//return
})//regionslist
return [list];//return
} //render
} //class
A limitation of this option is however that a key has to be added to the items as React will otherwise throw this warning in the console:
Warning: Each child in a list should have a unique "key" prop.
Note that even if you omit this key it will still render properly.
Initially, there was a limitation in React that you can only return a single element from render function but it has been changed after React 16, Now you can return the array of elements.
In your case, you are trying to array of the element without mentioning that it's an array due to which you have to group it using Fragment.
This issue can be solved in two ways:
Wrap array inside Fragment or any other container element (But in that case it will add one extra node to the DOM which is bad and to solve that issue Fragment's are introduced. for more details check here: https://reactjs.org/docs/fragments.html)
return (
<React.Fragment>
{ list }
</React.Fragment>
);
Return an array.
return ([ list ]);

Fuzzy search with Fuzzy.js and Tables? ReactJS

So I'm trying to use Fuzzy for my fuzzy search thing I have going on but it is very slow and doesn't seem like my states are updating on time. Here is a CodeSandbox with everything, and here is a snippet of my App.js:
const uuidv4 = require('uuid/v4');
var fuzzy = require('fuzzy');
console.log(fuzzy)
var searching = false;
class App extends Component {
constructor(props){
super(props);
this.state = {
searchWord: "",
searchMatches: []
}
this.fuzzySearch = this.fuzzySearch.bind(this);
this.handleChange = this.handleChange.bind(this);
}
//fuzzy search
fuzzySearch () {
var list = keywords.keywords;
var options = {
pre: '<b>'
, post: '</b>'
, extract: function(el) { return el.action; }
};
var results = fuzzy.filter(this.state.searchWord, list, options);
var matches = results.map(function(el) { return el.string; });
this.setState({searchMatches: matches});
console.log(this.state.searchMatches);
// [ '<b>a<c>o<n>ing', 'a mighty <b>ear <c>a<n>oe' ]
}
handleChange(event) {
this.setState({searchWord: event.target.value});
this.fuzzySearch()
}
render() {
const labelId = uuidv4();
return (
<div>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div className="form">
<Search handleChange={this.handleChange} searchWord={this.state.searchWord}/>
<WordTable searchMatches={this.state.searchMatches}/>
</div>
</div>
);
}
}
class Search extends Component {
constructor(props){
super(props);
this.state = {
}
}
render () {
return (
<div id="searchDiv">
<input id="searchBar" type="text" placeholder="Search Keyword and/or Action..." value={this.props.searchWord} name="searchWord" onChange={this.props.handleChange}/>
</div>
);
}
}
class WordTable extends Component {
constructor(props) {
super(props);
this.results = this.results.bind(this);
}
results() {
console.log("result");
console.log(this.props.searchMatches.length)
if(this.props.searchMatches.length > 0){
var matches = this.props.searchMatches;
return (
matches.map(p =>
<Row key={uuidv4()} action={p.action} keyword={p.keyword}/>
)
)
} else {
return(
keywords.keywords.map(p =>
<Row key={uuidv4()} action={p.action} keyword={p.keyword}/>
)
)
}
}
render () {
return (
<div id="table">
<table>
<thead>
<tr>
<th>Action</th>
<th>Keyword(s)</th>
</tr>
</thead>
<tbody>
{/*console.log(keywords.keywords[0])}
{keywords.keywords.map((keyword, action) =>
<Row keyword={keywords.keywords.keyword} action={keywords.keywords.action}/>
)*/
this.results()
}
</tbody>
</table>
{JSON.stringify(this.props.actions, null, " ")}
</div>
);
}
}
class Row extends Component {
render () {
return (
<tr>
<td key={uuidv4()} value={this.props.action}>{this.props.action}</td>
<td key={uuidv4()} value={this.props.keyword}>{this.props.keyword}</td>
</tr>
)
}
}
export default App;
I am not sure why my search is making my React app slow, it also doesn't seem like the matches or searchMathes variable/state are updating on time, but could it be just that console.log is doing something funky? I am new to React and JS so any explanation of what's going on or what you think might be going on would help a lot.
And on top of the search being very slow, my results don't show up in my WordTable table. Any ideas as to why?
And an explanation of my JSON, the actual file I am using has 6000+ objects in it that look like
{ "action": "this-is-the-action", "keyword": "this is the keyword"}
I just put some random ones in the CodeSandbox I made there, But the idea is that there are duplicate actions to different keywords. I hope that explains that a little better. Thanks!
There were a number of things wrong with your initial solution.
as I said in my comment, this.setState is asynchronous/batched
following,
this.setState({ searchWord: event.target.value })
this.fuzzySearch()
might not (aka probably wont) work. Why? Because the time between this.setState(...) and accessing this.state.searchWord in fuzzySearch is small enough that React probably hasn't reconciled the new state yet.
your WordTable component was super wonky. You were passing a string[], but it expected the original objects (with a shape like { action: string, keyword: string }). You were also re-rendering all options if there were no matches... not sure if that was intended behaviour but it made it seem like everything matched when, in fact, nothing did.
not really a problem so much as an optimisation but there's really no point in computing and storing the filtered list - just compute it and pass it down as a prop after searchWord has changed. This also takes away the race condition in #1.
Here's a working example with all of the above implemented (a cleaned up a little).

ReactJS: How to render rows dynamically

I am facing an issue to order the rows of a fetched API data dynamically, i.e if the rows data within the API is changed (+/- row) to render it automatically. I am trying to print the data fetched from an API into a table. Anyhow the columns are well printed dynamically, but the function that I am using for columns this.state.columns.map(( column, index ) => doesn't function in the same way for the rows. I think i am misleading the ES6 standard, but I am not sure. Here is how it's look like, if the rows are not hardcoded.
Here is my code sample:
class App extends React.Component
{
constructor()
{
super();
this.state = {
rows: [],
columns: []
}
}
componentDidMount()
{
fetch( "http://ickata.net/sag/api/staff/bonuses/" )
.then( function ( response )
{
return response.json();
} )
.then( data =>
{
this.setState( { rows: data.rows, columns: data.columns } );
} );
}
render()
{
return (
<div id="container" className="container">
<h1>Final Table with React JS</h1>
<table className="table">
<thead>
<tr> {
this.state.columns.map(( column, index ) =>
{
return ( <th>{column}</th> )
}
)
}
</tr>
</thead>
<tbody> {
this.state.rows.map(( row ) => (
<tr>
<td>{row[0]}</td>
<td>{row[1]}</td>
<td>{row[2]}</td>
<td>{row[3]}</td>
<td>{row[4]}</td>
<td>{row[5]}</td>
<td>{row[6]}</td>
<td>{row[7]}</td>
<td>{row[8]}</td>
<td>{row[9]}</td>
<td>{row[10]}</td>
<td>{row[11]}</td>
<td>{row[12]}</td>
<td>{row[13]}</td>
<td>{row[14]}</td>
<td>{row[15]}</td>
</tr>
) )
}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render( <div id="container"><App /></div>, document.querySelector( 'body' ) );
Instead, I was able to print the rows harcoded, if I give value to the 'td' elements, but I want to print it dynamically, in case the data within the API has been changed.
You are welcome to contribute directly to my Repo: Fetching API data into a table
Here is how looks like my example, when the rows values has been hardcoded within 'td' elements.
Any suggestions will be appreciated!
Just as you are iterating over columns and rows you could add an additional loop for iterating over row cells.
For example:
this.state.rows.map(row => (
<tr>{row.map(cell => (
<td>{cell}</td>
))}
</tr>
))
Note that you probably want to add a key prop:
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity

Categories