-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dynamic list of todos #2758
base: master
Are you sure you want to change the base?
dynamic list of todos #2758
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your hard work on this pull request! 😊 While your implementation meets many of the task requirements, there are a few critical issues that need to be addressed before we can approve it:
-
State Initialization with Props: Initializing state directly from props can lead to unexpected behavior. Please ensure that state is not initialized from props in the
TodoList.tsx
file. -
Dependency Arrays in
useEffect
: Ensure that dependencies inuseEffect
hooks are stable to avoid unnecessary re-renders or API calls. This includes stabilizing functions likeonFilterActive
,onFilterAll
, andonFilterCompleted
usinguseCallback
. -
Handling
selectedId
: Using0
as an initial value forselectedId
can cause issues if a todo with an ID of0
exists. Consider usingnull
to indicate no selection, and adjust your logic accordingly. -
Conditional Rendering and Classnames: Ensure that elements are conditionally rendered only when data is fully available, and consider using conditional classnames to improve user experience, such as visually indicating the selected todo.
These changes are important to ensure the robustness and clarity of your code. Once these issues are addressed, your pull request will be ready for approval. Keep up the great work! 🌟
✨ Please rate this review in the Platform's notification center. I will be grateful for your feedback! ✨
🔄 Fix the issues, commit the changes, and then re-request my review using the Re-request review
button in the Reviewers tab.
src/App.tsx
Outdated
const [allTodos, setAllTodos] = useState<Todo[]>([]); | ||
const [todos, setTodos] = useState<Todo[]>([]); | ||
const [loading, setLoading] = useState(false); | ||
const [selectedId, setSelectedId] = useState(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider initializing selectedId
with null
instead of 0
to clearly indicate that no todo is selected initially. This will also help in avoiding potential confusion with a todo having an ID of 0
.
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<TodoModal /> | ||
{selectedId && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using selectedId
directly in a boolean context may lead to unexpected behavior if a todo with an ID of 0
exists. Consider checking for selectedId !== 0
or using a more explicit condition.
onFilterCompleted, | ||
onFilterByTitle, | ||
}) => { | ||
const [isQuery, setIsQuery] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The isQuery
state is used to determine if there is a query present. However, since query.length > 0
can be directly checked where needed, you might consider removing isQuery
to simplify the state management.
break; | ||
} | ||
} | ||
}, [query, filterStatus, onFilterActive, onFilterAll, onFilterCompleted]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency array for useEffect
includes onFilterActive
, onFilterAll
, and onFilterCompleted
. Ensure these functions are stable (e.g., wrapped in useCallback
) to prevent unnecessary re-renders.
src/components/TodoList/TodoList.tsx
Outdated
); | ||
<tbody> | ||
{todos.map(todo => ( | ||
<tr data-cy="todo" key={todo.id} className=""> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a conditional class to the <tr>
element to visually indicate the selected todo. This can improve the user experience by clearly showing which todo is currently selected.
src/components/TodoList/TodoList.tsx
Outdated
data-cy="selectButton" | ||
className="button" | ||
type="button" | ||
onClick={() => handleSelect(todo.id)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The handleSelect
function is straightforward, but you might consider inlining this logic directly in the onClick
handler if it remains simple. This can reduce the number of function declarations and slightly improve readability.
setLoading(false); | ||
}); | ||
} | ||
}, [selectedTodo?.userId, selectedTodo]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency array for useEffect
includes selectedTodo?.userId
and selectedTodo
. Ensure that selectedTodo
is stable or derived from a stable source to prevent unnecessary API calls. Consider using id
as a dependency instead, since it directly determines the selected todo.
type="button" | ||
className="delete" | ||
data-cy="modal-close" | ||
onClick={() => onClose(0)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When closing the modal, consider passing null
or undefined
to onClose
instead of 0
to clearly indicate that no todo is selected. This aligns with the suggestion to initialize selectedId
with null
in the App.tsx
file.
|
||
{' by '} | ||
|
||
<a href="mailto:[email protected]">Leanne Graham</a> | ||
<a href={`mailto: ${data?.email}`}>{data?.name}</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure that data?.email
and data?.name
are available before rendering the link. You might want to conditionally render this part to avoid displaying an incomplete link if the data is not yet loaded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your work on the project has been reviewed, and I'm pleased to say that it meets the requirements! 🎉
You've effectively addressed the previous issues, particularly with the initialization of selectedId
and the use of useCallback
to stabilize functions. The components are well-structured, and the use of useEffect
is appropriate with stable dependencies. The conditional rendering and use of classnames
are implemented correctly, enhancing the user interface.
However, there are a few points to keep in mind for future improvements:
-
Dependency Arrays in
useEffect
: Ensure that all dependencies in youruseEffect
hooks are necessary to avoid unnecessary re-renders or re-fetching of data. This is crucial for maintaining performance and ensuring that your application behaves as expected. -
Conditional Rendering: Make sure that the logic for rendering components like
TodoModal
aligns with your application's requirements, especially when dealing with values like0
ornull
. -
Function Handling: Verify that functions like
onSelect
andonClose
are correctly updating the state in the parent component to maintain the intended flow of data.
Your attention to these details will help you refine your skills and produce even more robust code in the future. Keep up the great work, and continue learning from each project! 🚀
✨ Please rate this review in the Platform's notification center. I will be grateful for your feedback! ✨
const [filteredTodos, setFilteredTodos] = useState<Todo[]>([]); | ||
const [todos, setTodos] = useState<Todo[]>([]); | ||
const [loading, setLoading] = useState(false); | ||
const [selectedId, setSelectedId] = useState<number | null>(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job on using null
as the initial value for selectedId
. This helps avoid potential issues with a todo having an ID of 0
.
setTodos(todosFromServer); | ||
setLoading(false); | ||
}); | ||
}, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency array for useEffect
is empty, which means this effect will only run once when the component mounts. Ensure this is the intended behavior, as it will not re-fetch todos if any dependencies change.
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<TodoModal /> | ||
{selectedId && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure that the conditional rendering of TodoModal
aligns with your application's logic. Currently, it renders when selectedId
is truthy, which means it won't render if selectedId
is 0
or null
. This is fine if null
is intended to represent no selection.
onFilterActive, | ||
onFilterCompleted, | ||
onFilterByTitle, | ||
]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The useEffect
hook has a comprehensive dependency array, which is good for ensuring that the effect runs when any of these dependencies change. Make sure that all dependencies are necessary to avoid unnecessary re-renders. In this case, it seems appropriate as each dependency is related to the filtering logic.
src/components/TodoList/TodoList.tsx
Outdated
data-cy="todo" | ||
key={todo.id} | ||
className={classNames({ | ||
'has-background-info-light': selectedId === todo.id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of classNames
to conditionally apply the has-background-info-light
class based on selectedId
is correct. This helps visually indicate the selected todo.
src/components/TodoList/TodoList.tsx
Outdated
data-cy="selectButton" | ||
className="button" | ||
type="button" | ||
onClick={() => onSelect(todo.id)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure that the onSelect
function is correctly updating the selectedId
in the parent component. This line correctly calls onSelect
with the todo.id
when the button is clicked.
setLoading(false); | ||
}); | ||
} | ||
}, [selectedTodo?.userId, selectedTodo]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The useEffect
hook includes selectedTodo?.userId
and selectedTodo
as dependencies. Ensure that these dependencies are necessary to avoid unnecessary re-fetching of user data. In this case, it seems appropriate as it ensures data is fetched when the selected todo changes.
type="button" | ||
className="delete" | ||
data-cy="modal-close" | ||
onClick={() => onClose(null)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onClose
function is correctly called with null
to reset the selection when the modal is closed. Ensure that this logic is correctly handled in the parent component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Almost done
src/App.tsx
Outdated
const completedTodos = allTodos.filter(todo => todo.completed); | ||
|
||
setFilteredTodos(completedTodos); | ||
setTodos(completedTodos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's enough to have one state for filtered todos
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried, but If I remove filteredTodos, the search through the input field always works on all todos, even if I initially selected "Active" or "Completed". And If I use todos instead of filteredTodos, then when I delete a few letters to modify the search, the list is no longer filtered.
} | ||
|
||
useEffect(() => { | ||
if (filterStatus === 'all') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using an enum for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job 👍
Let's improve your code
src/App.tsx
Outdated
const [allTodos, setAllTodos] = useState<Todo[]>([]); | ||
const [filteredTodos, setFilteredTodos] = useState<Todo[]>([]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant states
const [allTodos, setAllTodos] = useState<Todo[]>([]); | |
const [filteredTodos, setFilteredTodos] = useState<Todo[]>([]); |
src/App.tsx
Outdated
getTodos().then(todosFromServer => { | ||
setAllTodos(todosFromServer); | ||
setFilteredTodos(todosFromServer); | ||
setTodos(todosFromServer); | ||
setLoading(false); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getTodos().then(todosFromServer => { | |
setAllTodos(todosFromServer); | |
setFilteredTodos(todosFromServer); | |
setTodos(todosFromServer); | |
setLoading(false); | |
}); | |
getTodos() | |
.then(setTodos) | |
.finally(() => setLoading(false)); |
enum FilterStatus { | ||
All = 'all', | ||
Active = 'active', | ||
Completed = 'completed', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move enum to the separate file
src/components/TodoList/TodoList.tsx
Outdated
<tr | ||
data-cy="todo" | ||
key={todo.id} | ||
className={classNames({ | ||
'has-background-info-light': selectedId === todo.id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this logic to the PostItem
component
src/components/TodoList/TodoList.tsx
Outdated
'has-text-danger': !todo.completed, | ||
})} | ||
> | ||
{todo.title} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use destructuring for todo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! 🔥
https://yzhyhaliuk.github.io/react_dynamic-list-of-todos/