33
loading...
This website collects cookies to deliver better user experience
QuizList
component receives a list of quizzesquery
keeps track of the search inputsortBy
keeps track of the sorting optionfilteredQuizzes
keeps track of the manipulated copy of quizzes to be displayed
const QuizList = ({ quizzes }: { quizzes: Quiz[] }): JSX.Element => {
const [query, setQuery] = useState('')
const [sortBy, setSortBy] = useState('title')
const [filteredQuizzes, setFilteredQuizzes] = useState<Quiz[]>([])
useEffect(() => {
let result = quizzes
if (sortBy === 'title') {
result.sort((a, b) => a.title.localeCompare(b.title))
} else {
result.sort((a, b) => a.week - b.week)
}
if (query.trim() === '') {
setFilteredQuizzes(result)
} else {
setFilteredQuizzes(
result.filter((quiz) => quiz.title.toLowerCase().includes(query.toLowerCase()))
)
}
}
}, [query, quizzes, sortBy])
return (
<div>
<Search query={query} setQuery={setQuery} />
<RadioGroup onChange={setSortBy} value={sortBy}>
<Stack direction="row">
<span>Sort by:</span>
<Radio value="title">Title</Radio>
<Radio value="week">Week</Radio>
</Stack>
</RadioGroup>
<div>
{filteredQuizzes.map((quiz) => {
return <QuizItemCard key={quiz.id} quiz={quiz} />
})}
</div>
</div>
)
let
instead of const
, they always surprised me with a hidden bug.setFilteredQuizzes
. Coincidentally, the article that I planned to write this week was related to the official React.js FAQ and reading its section on Component State gave me an idea on how to fix the state update.setState
operations are not immediately invoked and inappropriate usage will result in unintended consequences. Quoting an example code snippet directly from the document:incrementCount() {
this.setState((state) => {
// Important: read `state` instead of `this.state` when updating.
return {count: state.count + 1}
});
}
handleSomething() {
// Let's say `this.state.count` starts at 0.
this.incrementCount();
this.incrementCount();
this.incrementCount();
// If you read `this.state.count` now, it would still be 0.
// But when React re-renders the component, it will be 3.
}
filteredQuizzes
is referenced. Other alternative solutions include updating the filteredQuizzes
in event handlers of the sorting radio buttons instead of keeping track of the sorting state.const QuizList = ({ quizzes }: { quizzes: Quiz[] }): JSX.Element => {
const [query, setQuery] = useState('')
const [sortBy, setSortBy] = useState('title')
const [filteredQuizzes, setFilteredQuizzes] = useState<Quiz[]>([])
useEffect(() => {
if (sortBy === 'title') {
setFilteredQuizzes(() => [...quizzes].sort((a, b) => a.title.localeCompare(b.title)))
} else {
setFilteredQuizzes(() => [...quizzes].sort((a, b) => a.week - b.week))
)
}
if (query.trim() === '') {
setFilteredQuizzes((filteredQuizzes) => filteredQuizzes)
} else {
setFilteredQuizzes((filteredQuizzes) =>
filteredQuizzes.filter((quiz) => quiz.title.toLowerCase().includes(query.toLowerCase()))
)
}
}, [query, quizzes, sortBy])
return (
<div>
<Search query={query} setQuery={setQuery} />
<RadioGroup onChange={setSortBy} value={sortBy}>
<Stack direction="row">
<span>Sort by:</span>
<Radio value="title">Title</Radio>
<Radio value="week">Week</Radio>
</Stack>
</RadioGroup>
<div>
{filteredQuizzes.map((quiz) => {
return <QuizItemCard key={quiz.id} quiz={quiz} />
})}
</div>
</div>
)