I am creating a simple tracker which records all the activities done. It is my first project in react. I have created three state one for storing all the items(name of state is list), one for pending items(name of state is pending) , one for completed items(name of state is completed). The items have a button which when clicked marks it into done state and vice-versa. It is completely rendering items for main list. But for other two its not rendering. When I am checking with react developer tools, it is working fine, i.e. it is adding to pending list or completed list as it should. But it is not compiling them on screen. list is already filled with items. I have added all the code for just in case.
function Filters(props){
const [completed, setCompleted] = useState([]);
const [pending, setPending] = useState([]);
const [state, setState] = useState("None");
const [list,setList] = useState([]);
function viewState(){
setState("View-all");
}
//it is getting the clicked item id and marking it complete in main list
function markComplete(id){
list.map((items,index)=>{
if(index===id){
if(items.done===true)
items.done = false;
else{
items.done=true;
}
}
})
}
//i am simply scanning the main list and the items which are pending will be added to this list. //this happens whenever the person click on pending button
function pendingState(){
setState("pending-all");
setPending([]);
list.map(items=>{
if(items.done!==true){
setPending(prev=>{
return [...prev,items];
})
}
})
}
function completedState(){
setState("completed-all");
setCompleted([]);
list.map(items=>{
if(items.done===true){
setCompleted(prev=>{
return [...prev,items];
})
}
})
}
return (
<div>
<div className="input-section">
<Note setList={setList} />
</div>
<button type="button" onClick={viewState} >View All</button>
<button type="button" onClick={completedState}>Completed</button>
<button type="button" onClick={pendingState}>Pending</button>
<div>
{
(()=>{
if(state==="View-all")
{
return (
<div>
<h1>Working {completed}</h1>
{(list).map((items,index)=>
{
return (
<Excercise
key={index}
id={index}
title={items.name}
list={props.list}
setList={props.setList}
content={items.desp}
markComplete={markComplete}
/>
)
})}
</div>
)
}
else if(state==="completed-all")
{
return (
<div>
{completed.map((items,index)=>{
<Excercise
key={index}
id={index}
title={items.name}
list={props.list}
setList={props.setList}
content={items.desp}
markComplete={markComplete}
/>
})}
</div>
)
}
})()
}
</div>
</div>);
}
Kindly help. Thank you.
CodePudding user response:
I think you've overcomplicated things a bit. You only need one array to store the exercises in, the "pending" and "completed" states are easily derived from the list
state and the state
filter state value.
Issues
markComplete
callback is mutating thelist
state. When updating thelist
state not only is a new array reference necessary, but also new element object references are necessary for the elements that are being updated.- Uses poor boolean comparisons to set a boolean value. You can either toggle a boolean or set the value to the result of a boolean expression.
- Use the
viewState
,pendingState
, andcompletedState
handlers to simply set the filter value, and then derive the computed state when rendering by adding an inlinefilter
function. - Use the exercise
id
property as a React key and as the property used for toggling the completed (done
) state.
Solution
function Filters(props) {
const [state, setState] = useState("None");
const [list, setList] = useState([
...
]);
function viewState() {
setState("View-all");
}
function pendingState() {
setState("pending-all");
}
function completedState() {
setState("completed-all");
}
const markComplete = (id) => {
setList((list) =>
list.map((item) =>
item.id === id
? {
...item,
done: !item.done
}
: item
)
);
};
return (
<div>
<div className="input-section">
<Note setList={setList} />
</div>
<button type="button" onClick={viewState}>
View All
</button>
<button type="button" onClick={completedState}>
Completed
</button>
<button type="button" onClick={pendingState}>
Pending
</button>
<div>
{list
.filter((item) => {
if (state === "pending-all") {
return !item.done;
} else if (state === "completed-all") {
return item.done;
}
return true;
})
.map((item) => (
<Excercise
key={item.id}
id={item.id}
done={item.done}
title={item.name}
content={item.desp}
markComplete={markComplete}
/>
))}
</div>
</div>
);
}
CodePudding user response:
try to add dependecies in useEffect
CodePudding user response:
The reason of your app not updating is because when your state changes react is not re-rendering it again.
so use useEffect
, there are many hooks which can be used as per requirement.
try putting this line of code
useEffect( ( ) => {
console.log( 'Check console' );
}, [ dependency_here ] );
in dependency_here
try adding state
, completed
, pending
one by one and see the result.
You can also add multiple dependencies like [ state, pending, etc.. ]
;
Try on your own you'll understand it faster.
Hope hint will help you!
CodePudding user response:
in this function you are mutating a state, so in order to do so you need to use the setState function, in this case, it will be setList().
function markComplete(id){
list.map((items,index)=>{
if(index===id){
if(items.done===true)
items.done = false;
else{
items.done=true;
}
}
})
}
So a better way to implement this function could be, and remember, everything time you need to update a state you don't want to change the state directly, instead, you should make a copy and set the state to that copy
function markComplete(id){
const newList = [...list];
newList.map((items,index)=>{
if(index===id){
if(items.done===true)
items.done = false;
else{
items.done=true;
}
}
}
setList(newList)
}