Home > Software engineering >  React conditional re-rerender of an array based on filter
React conditional re-rerender of an array based on filter

Time:11-13

I’m running into an error that I could use some help on

Basically, I have a react app that is executing an HTTP call, receiving an array of data, and saving that into a state variable called ‘tasks’. Each object in that array has a key called ‘completed’. I also have a checkbox on the page called ‘Show All’ that toggles another state variable called showAll. The idea is by default all tasks should be shown however if a user toggles this checkbox, only the incomplete tasks (completed==false) should be shown. I can get all tasks to display but can’t get the conditional render to work based on the checkbox click

Here’s how I’m implementing this. I have the HTTP call executed on the page load using a useEffect hook and available to be called as a function from other change handlers (edits etc.)

Before I call the main return function in a functional component, I’m executing a conditional to check the status of ’ShowAll’ and filter the array if it's false. This is resulting in too many re-render errors. Any suggestions on how to fix it?

See simplified Code Below

const MainPage = () => {


    const [tasks, setTasks] = useState([]); //tasks
    const [showAll, setShowAll] = useState(true);  //this is state for the checkbox (show all or just incomplete)

    useEffect( ()=> {
        axios.get('api/tasks/')
            .then( response => {   //this is the chained API call
                setTasks(response.data.tasks); 
            })
            .catch(err => {
                console.log('error');
            })
        
    }, []);

    const fetchItems = (cat_id) => {  
        axios.get('/api/tasks/')
        .then( response => {
                setTasks(response.data.tasks);
        })
        .catch(err => {
            console.log('error');
        })
    }; 


    //change the checkbox state
    const handleCheckboxChange = (e) => {
        setShowAll(!showAll)
        console.log('Checkbox: ', showAll)
    };

    
    //this part updates the tasks to be filtered down to just the incomplete ones based on the checkbox value
    if (showAll === false) {
        setTasks(tasks.filter(v => v['completed']===false)); //only show incomplete tasks
    }
    

    return (
        <div>
           
           <label className="checkb">
                <input 
                name="show_all"
                id="show_all"
                type="checkbox"
                checked={showAll}
                onChange={handleCheckboxChange}
                /> Show all
            </label>

            <br/>
            { tasks && tasks.map((task, index) => {
                return (
                    <div key={index} className="task-wrapper flex-wrapper">
                    
                        <div > 
                                { task.completed === false ? (
                                <span> {index  1}. {task.task_description} </span> ) : 
                                (<strike> {index  1}. {task.task_description} </strike>)  }                               
                        </div>
                        
                        <div>
                            <button 
                                onClick={()=> modalClick(task)}
                                className="btn btn-sm btn-outline-warning">Edit</button>
                            <span> &nbsp;  </span>
                        
                            
                        </div>
                    </div>
                )
                })}
                
         
        </div>
    );

};

export default MainPage;

Thanks

CodePudding user response:

Two things to fix:

Use the checked property on event.target to update the state:

const handleCheckboxChange = ({target: { checked }}) => {
  setShowAll(checked)
};

Filter as you want but don't update the state right before returning the JSX as that would trigger a rerender and start an infinite loop:

let filteredTasks = tasks;

if (!showAll) {
      filteredTasks = tasks?.filter(v => !v.completed));
}

and in the JSX:

{ tasks && tasks.map should be {filteredTasks?.map(...

CodePudding user response:

use e.target.value and useEffect :

      //change the checkbox state
    const handleCheckboxChange = (e) => {
        setShowAll(e.target.checked)
        console.log('Checkbox: ', showAll)
 if (!e.target.checked) {
let list =tasks.filter(v => v.completed===false);
        setTasks(list ); //only show incomplete tasks
    }
    };



or

      //change the checkbox state
        const handleCheckboxChange = (e) => {
            setShowAll(e.target.checked)
            console.log('Checkbox: ', showAll)
   
        };

useEffect(()=>{
  if (showAll === false) {
    let list =tasks.filter(v => v.completed===false);
            setTasks(list ); //only show incomplete tasks
        }
},[showAll])
  • Related