I have a ponent that displays a list of Cards. I'm trying to sort the table rows but running into some issues. When i go to the page i'm getting the following error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
and it's pointing to this line
setData(_.sortBy(filteredData.reverse()));
here's my full ponent code. can anyone see a problem with what I'm trying to do?
import React, { useState } from "react";
import Search from "./Search";
import TimeAgo from "react-timeago";
import { useSelector, useDispatch, connect } from "react-redux";
import { Table } from "semantic-ui-react";
import { searchChange } from "../reducers/searchReducer";
import _ from "lodash";
// import { useField } from "../hooks";
const searchCards = ({ baseball, search }) => {
return search
? baseball.filter(a =>
a.title[0].toLowerCase().includes(search.toLowerCase())
)
: baseball;
};
const Cards = props => {
const [column, setColumn] = useState(null);
const [direction, setDirection] = useState(null);
const [filteredData, setData] = useState(props.cardsToShow);
const handleSort = clickedColumn => {
if (column !== clickedColumn) {
setColumn(clickedColumn);
setData(_.sortBy(filteredData, [clickedColumn]));
setDirection("ascending");
return;
}
setData(_.sortBy(filteredData.reverse()));
direction === "ascending"
? setDirection("descending")
: setDirection("ascending");
};
return (
<>
<div>
<Search />
<h3>Vintage Card Search</h3>
<Table sortable celled fixed striped>
<Table.Header>
<Table.Row>
<Table.HeaderCell
sorted={column === "title" ? direction : null}
onClick={handleSort("title")}
>
Card Title
</Table.HeaderCell>
<Table.HeaderCell># Bids</Table.HeaderCell>
<Table.HeaderCell>Watchers</Table.HeaderCell>
<Table.HeaderCell>Price</Table.HeaderCell>
<Table.HeaderCell>Time Left</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{props.cardsToShow.map(card => (
<>
<Table.Row key={card.id}>
<Table.Cell>{card.title}</Table.Cell>
<Table.Cell>
{card.sellingStatus[0].bidCount
? card.sellingStatus[0].bidCount
: 0}
</Table.Cell>
<Table.Cell>
{card.listingInfo[0].watchCount
? card.listingInfo[0].watchCount
: 0}
</Table.Cell>
<Table.Cell>
$
{card.sellingStatus &&
card.sellingStatus[0].currentPrice[0]["__value__"]}
</Table.Cell>
<Table.Cell>
<TimeAgo
date={new Date(
card.listingInfo && card.listingInfo[0].endTime
).toLocaleDateString()}
/>
</Table.Cell>
</Table.Row>
</>
))}
</Table.Body>
</Table>
</div>
</>
);
};
const mapStateToProps = state => {
return {
baseball: state.baseball,
search: state.search,
cardsToShow: searchCards(state)
};
};
const mapDispatchToProps = {
searchChange
};
export default connect(mapStateToProps, mapDispatchToProps)(Cards);
// export default Cards;
I have a ponent that displays a list of Cards. I'm trying to sort the table rows but running into some issues. When i go to the page i'm getting the following error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
and it's pointing to this line
setData(_.sortBy(filteredData.reverse()));
here's my full ponent code. can anyone see a problem with what I'm trying to do?
import React, { useState } from "react";
import Search from "./Search";
import TimeAgo from "react-timeago";
import { useSelector, useDispatch, connect } from "react-redux";
import { Table } from "semantic-ui-react";
import { searchChange } from "../reducers/searchReducer";
import _ from "lodash";
// import { useField } from "../hooks";
const searchCards = ({ baseball, search }) => {
return search
? baseball.filter(a =>
a.title[0].toLowerCase().includes(search.toLowerCase())
)
: baseball;
};
const Cards = props => {
const [column, setColumn] = useState(null);
const [direction, setDirection] = useState(null);
const [filteredData, setData] = useState(props.cardsToShow);
const handleSort = clickedColumn => {
if (column !== clickedColumn) {
setColumn(clickedColumn);
setData(_.sortBy(filteredData, [clickedColumn]));
setDirection("ascending");
return;
}
setData(_.sortBy(filteredData.reverse()));
direction === "ascending"
? setDirection("descending")
: setDirection("ascending");
};
return (
<>
<div>
<Search />
<h3>Vintage Card Search</h3>
<Table sortable celled fixed striped>
<Table.Header>
<Table.Row>
<Table.HeaderCell
sorted={column === "title" ? direction : null}
onClick={handleSort("title")}
>
Card Title
</Table.HeaderCell>
<Table.HeaderCell># Bids</Table.HeaderCell>
<Table.HeaderCell>Watchers</Table.HeaderCell>
<Table.HeaderCell>Price</Table.HeaderCell>
<Table.HeaderCell>Time Left</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{props.cardsToShow.map(card => (
<>
<Table.Row key={card.id}>
<Table.Cell>{card.title}</Table.Cell>
<Table.Cell>
{card.sellingStatus[0].bidCount
? card.sellingStatus[0].bidCount
: 0}
</Table.Cell>
<Table.Cell>
{card.listingInfo[0].watchCount
? card.listingInfo[0].watchCount
: 0}
</Table.Cell>
<Table.Cell>
$
{card.sellingStatus &&
card.sellingStatus[0].currentPrice[0]["__value__"]}
</Table.Cell>
<Table.Cell>
<TimeAgo
date={new Date(
card.listingInfo && card.listingInfo[0].endTime
).toLocaleDateString()}
/>
</Table.Cell>
</Table.Row>
</>
))}
</Table.Body>
</Table>
</div>
</>
);
};
const mapStateToProps = state => {
return {
baseball: state.baseball,
search: state.search,
cardsToShow: searchCards(state)
};
};
const mapDispatchToProps = {
searchChange
};
export default connect(mapStateToProps, mapDispatchToProps)(Cards);
// export default Cards;
Share
Improve this question
asked Nov 23, 2019 at 22:53
John RogersonJohn Rogerson
1,1933 gold badges22 silver badges53 bronze badges
2 Answers
Reset to default 5Yachaka has already pointed out the incorrect line, but their answer doesn't explain what the issue is.
When you pass props in React with prop={expression}
, the expression in the brackets gets evaluated, much like function arguments are evaluated when they are passed. Hence, whenever the ponent is rendered, handleSort("title")
is called. This function then causes the props to be updated, and the ponent is re-rendered, causing the cycle to repeat indefinitely.
So the problem is that, instead of passing a function that should be called when the button is clicked, you call that function (with handleSort("title")
), which results in undefined
, and causes a feedback loop.
Instead you should use an expression that returns a function. The most concise way of doing that in JavaScript is an arrow function, as Yachaka mentioned () => handleSort("title")
. This evaluates to a function that calls handleSort
.
Change this line:
onClick={handleSort("title")}
by
onClick={() => handleSort("title")}
EDIT: Reinis has written a nice explanation below!