I'm currently encountering a problem when i'm calling a for loop in a functional/class component in React. For exemple if I want to create a simple function to render multiple td tr in a table React is going to infinite call the function.
class ReserveView extends Component {
createTable() {
const table = []
for (let i = 0; i < 3; i + 1) {
const children = []
for (let j = 0; j < 5; j + 1) {
children.push(<td>{`Column ${j + 1}`}</td>)
}
table.push(<tr>{children}</tr>)
}
return table
}
render() {
return (
<div>
<table>
{this.createTable()}
</table>
</div>
)
}
If i console.log(j) the console will output
this
I'm using React for a year now and it's the first time I'm having this issue. Thanks a lot for your help
Oh yeah, your problem lies in the fact that you never increase the i and j loop variant.
you need to do i++ and j++ instead of j + 1 :)
Change your +1s to ++
for (let i = 0; i < 3; i++) {
const children = []
for (let j = 0; j < 5; j++) {
children.push(<td>{`Column ${j + 1}`}</td>)
}
table.push(<tr>{children}</tr>)
}
return table
The correct answer has been posted above. Just a note that you may try ES6 map() function. It would be very useful when you are coding with React.
createTable2() {
const rows = Array(3).fill(null);
const columns = Array(5).fill(null);
return rows.map((item, ridx) => (
<tr>
{columns.map((item, cidx) => (
<td>{`Row ${ridx + 1} - Column ${cidx + 1}`}</td>
))}
</tr>
));
}
Check out the demo here: https://codesandbox.io/s/table-demo-fgd9i
Related
I am new to React and working on a project to get a better grasp of all its concepts. I am currently building a time-tracking application, that allows users to track time across tasks from different projects.
I am using Redux and storing in my app state a list of Projects each with a list of Tasks. Each task has a totalDurationInSeconds property.
I want to create a Reports page. Currently on the reports page, I only want to display the total duration in seconds across all projects. When I first start the application, the time is 0. If I add a task to one of the projects, the time gets updated.
However, when I add a second task to either the same project or a different project, the value does not get updated and it still only displays the duration of the first task.
const ReportsPage: React.FC<Props> = (props): React.ReactElement => {
const [totalDuration, setTotalDuration] = useState(0);
useEffect(() => {
for (let i = 0; i < props.projects.length; i++) {
for (let j = 0; i < props.projects[i].tasks.length; i++) {
setTotalDuration(totalDuration + props.projects[i].tasks[j].totalDurationInSeconds);
}
}
}, [])
return (
<div>
<p>Total time spent across all projects : {totalDuration}</p>
</div>
);
};
My component is connected to the ReduxStore and Props is of type StateProps & ReportsPageProps.
Your inner loop condition and increment is using i instead of j
this is what you want:
for (let i = 0; i < props.projects.length; i++) {
for (let j = 0; j < props.projects[i].tasks.length; j++) {
setTotalDuration(totalDuration + props.projects[i].tasks[j].totalDurationInSeconds);
}
}
When useEffect function used without any dependency it executed one time, but you want the totalDuration will update when any task added.
useEffect(() => {
for (let i = 0; i < props.projects.length; i++) {
for (let j = 0; j < props.projects[i].tasks.length; j++) {
setTotalDuration(totalDuration + props.projects[i].tasks[j].totalDurationInSeconds);
}
}
}, [props.projects])
Based on a snippet of original r3f-example found in PointCloud.js
Tested by myself, this above original component is able to render pointcloud by pushing individual x y z value into the for-loop in Particle() function.
I modified it and added a `fetch()' method to retrieve a custom data txt file, snippet as shown below,
...
export function Particles() {
const [positions, colors] = useMemo(() => {
let positions = [], colors = []
positions.length = 3
colors.length = 3
const HEADER_SIZE = 4;
let stream, longArray, len;
let clusterCount ;
let xy_size ;
let clusterSize = [];
let XY_arr = [];
fetch(map)
.then((r) => r.text())
.then(text => {
stream = text.toString().split("\n"); // split by next line
longArray = stream.slice(2,); // remove header from main longArray
len = longArray.length;
for (let i = 0, count = 0; i < len; i += HEADER_SIZE ) {
xy_size = longArray.slice((i + HEADER_SIZE - 1), (i + HEADER_SIZE));
XY_arr.push(longArray.slice((i + HEADER_SIZE ), (i + HEADER_SIZE + xy_size*2)));
console.log(" Points in PointCloud " + count + ": " + xy_size );
clusterSize.push(xy_size);
clusterCount = count;
i += xy_size*2;
count ++;
}
for (let i = 0; i < (clusterCount-2); i++) {
for (let j = 0; j < clusterSize[i]*2; j+=2) {
positions.push( XY_arr[i][j] )
positions.push(0)
positions.push( XY_arr[i][j+1] )
colors.push(1)
colors.push(0.5)
colors.push(0.5)
console.log( XY_arr[i][j] );
}
}
}
)
return [new Float32Array(positions), new Float32Array(colors)]
}, [])
...
...
, map is the custom text file in string, with single data line-by-line
The fetch() method is able to read a custom pointcloud file into XY_arr as an object of Array(). I have checked that XY_arr[i][j] in the nested-forloop are able to return correct x and z value in console.
Current problem is that no pointcloud being rendered onto <Canvas />
Is the problem caused by position.push() nested loop being inside of 'fetch()' method ? And how to resolve. Thank you.
better use const [state, set] = useState() and then fetch in useEffect calling "set" when you're done. putting an async fetch request inside useMemo is practically a side-effect in the render function - which isn't good, nor will it work like that.
renderItems() {
const {items} = this.state;
const results = [];
for (let i = 0; i < items.length; i++) {
results.push(
<tr>
{
for (let j = 0; j < 8; j++) {
}
}
</tr>
)
}
}
Basically, I have an array of 32 items and would like to break it into 8 items per row (overall will be 4 rows), but the above code gives error for the inner loop inside <tr> which is correct (and im not sure how to correct it).
What would be the correct way in React to dynamically create 4 rows and put 8 tds inside each row?
You can't use a for loop directly inside the {} in JSX since it expects an expression.
You could do the for loop outside of the JSX, or chunk the items array into arrays of length 8 and map over those.
Example
renderItems() {
const items = [...this.state.items];
const chunks = [];
while (items.length) {
chunks.push(items.splice(0, 8));
}
return chunks.map(chunk => (
<tr>{chunk.map(item => <td>{item}</td>)}</tr>
));
}
This is by no means an elegant solution, but it works:
renderItems() {
const { items } = this.state;
// first, create all the <td> elements and store them in a list
let allTDs = []
for (let i = 0; i < items.length; i++) {
allTDs.push(<td key={ items[i] }> { items[i] } </td>)
}
//then break the list into four rows and keep them in separate lists
let tr1 = [];
let tr2 = [];
let tr3 = [];
let tr4 = [];
// keep count for each row
let count = 0
for (let td of allTDs) {
//first eight goes to the first row, second eight to the second etc.
if(count < 8) {
tr1.push(td)
} else if (count < 16) {
tr2.push(td)
} else if (count < 24) {
tr3.push(td)
} else if (count < 32) {
tr4.push(td)
}
count += 1;
}
// bring all four rows together in a list so that you can return that list
let allTRs = [];
allTRs.push(tr1, tr2, tr3, tr4)
return allTRs
}
render() {
return (
<div>
<table>
<tbody>
// serve each row by calling the renderItems() method and indexing
// the relevant row
<tr>{ this.renderItems()[0] }</tr>
<tr>{ this.renderItems()[1] }</tr>
<tr>{ this.renderItems()[2] }</tr>
<tr>{ this.renderItems()[3] }</tr>
</tbody>
</table>
</div>
)
}
I wrote this react component
class Row extends React.Component {
constructor(props) {
super(props);
this.style = {
display: "flex"
}
}
render(){
var rowcells = [];
for(let i = 0; i < 7; i ++) {
console.log("foo " + this.props.cells)
rowcells.push(<Cell key={i} col ={i} row={this.props.row} cell={this.props.cells[i]} handleClick={this.props.handleClick}/>)
}
return (
<div style = {this.style}>
{rowcells}
</div>
)
}
}
this component is called only once in my application like
for(let i = 6; i > 0; i--) {
rows.push(<Row key={i} row={i} cells={this.props.cells[i]} handleClick={this.props.handleClick}/>)
}
So you can see that the props.cells is defined. but when my code runs it fails by at the line this.props.cells[I] by saying its undefined
https://codepen.io/knows_not_much/pen/dddNRZ?editors=1111
This is a example from the react tutorial here
https://courses.edx.org/courses/course-v1:Microsoft+DEV281x+1T2018/courseware/404dd3a7096e46c5b4672e26e5a5b404/70c54b9d11ef4439a2996f9826ba6375/?child=last
The only difference between the sample and my code is that I am closing the component style rather than function style for react components.
Your for loop logic when creating Row components (inside your Board Component) has an error: You're accessing a item of the cells array which does not exist. It should be let i = 5 since the array has six elements and is 0 indexed not let i = 6.
Couple of console.log statements helped me to the issue.
In the code below you're not iterating over the first item of the array which is index 0, and also as #Govind suggested the i=6 must be i=5, so this:
for(let i = 6; i > 0; i--) {
rows.push(<Row key={i} row={i} cells={this.props.cells[i]} handleClick={this.props.handleClick}/>)
}
must be this:
for(let i = 5; i >= 0; i--) { //notice the greater or equal sign (>=)
rows.push(<Row key={i} row={i} cells={this.props.cells[i]} handleClick={this.props.handleClick}/>)
}
See it in action
I am trying to get a simple ES6 for-loop working but cant figure out why its not running.
I've copied an example from developer.mozilla docs and I've also tried it with the eslinter version which is below:
I have also added a let i = 0; above. All it renders/logs is i = 0 and wont increment.
the eslint version is here: eslint site
for (i = 0; i < 10; i += 1) {
console.log('i', i);
return <p>component {i}</p>;
}
Edit: ok got the values coming back in the log as i=0, i=1, etc... but to get them into a component each? i tried the push into array and mapping through to get the components out but i get no error and nothing appearing, even if i try just getting a value out.
const nbPageArray = [];
let i = 0;
for (i = 0; i < nbPages; i += 1) {
console.log('i', i);
nbPageArray.push(<p>component {i}</p>);
}
console.log('array', nbPageArray);
nbPageArray.map(a => <p>{a.type}</p>);
}
final working version:
const nbPageArray = [];
for (let i = 0; i < nbPages; i += 1) {
nbPageArray.push({ page: i + 1 });
}
return nbPageArray.map(a =>
<li className="page-item"><a className="page-link">{a.page}</a></li>,
);
Main issue is i += 10;
That should be 1 += 1;
And You should return array of elements :
var p_tags = [];
for (i = 0; i < 10; i += 1) {
console.log('i', i);
p_tags.push(<p>component {i}</p>);
}
return p_tags;
Edited question's answer :
First Error:
const nbPageArray = []; should be var nbPageArray = [];
Second You are not returning the array so change your code to this
return nbPageArray.map(a => <p>{a.type}</p>);
If you return from your for loop, you will exit the current function, you are also incrementing i by 10 each trip so you will exit the loop after one round either way.
If you are trying to print a string with the value of i ten times you could try using template string like so:
for (i = 0; i < 10; i += 1) {
console.log('i', i);
console.log(`<p>component ${i}</p>`);
}
you are returning from the loop and also incrementing by 10. The loop will execute only once.
As said in the comments, the return inside the for loop is going to exit from the function at the first iteration.
You can do something like this instead:
const result = Array(10).fill().map((_, i) =>
<p>component {i}</p>
);
Or
const result = [...Array(10)].map((_, i) =>
<p>component {i}</p>
);