In React I have an array of objects as a state. Let's say they are "todos". I also have two divs. One div is for incomplete tasks, second is for completed tasks. During mapping I'm using <Todo/> component.
const [todos, setTodos] = useState([
{id: 1, text: 'Lorem', isComplete: true},
{id: 2, text: 'Ipsum', isComplete: true}],
{id: 3, text: 'Test', isComplete: false})
]);
in return I have an HTML structure:
return (
<div>
<h1>Todos</h1>
<p>My todos for today:</p>
<div id="incomplete"></div>
<p>Todos that've been already done:</p>
<div id="complete"></div>
</div> )
Mapping looks something like this:
todos.map(todo => {
if(todo.isComplete){
return <Todo
id={todo.id}
text={todo.text}
isComplete={todo.isComplete}/>
// AND HERE I WANT TO PUSH THIS COMPONENT INTO div#complete.
} })
I'm expecting something like this:
return (
<div>
<h1>Todos</h1>
<p>My todos for today:</p>
<div id="incomplete">
<div className="task">
<p>Test</p>
</div>
<div className="task">
<p>Ipsum</p>
</div>
</div>
<p>Todos that've been already done:</p>
<div id="complete">
<div className="task">
<p>Lorem</p>
</div>
</div>
</div> )
CodePudding user response:
Use filter()
before map()
to get only the (in)/complete:
return (
<div>
<h1>Todos</h1>
<p>My todos for today:</p>
<div id="incomplete">
{todos.filter(t => !t.isComplete).map(e => <Todo ...{} />)}
</div>
<p>Todos that've been already done:</p>
<div id="complete">
{todos.filter(t => t.isComplete).map(e => <Todo ...{} />)}
</div>
</div>
)
React Demo:
const { useState } = React;
const Todo = (props) => {
return <p>{props.text}</p>;
}
const Example = () => {
const [todos, setTodos] = useState([
{id: 1, text: 'Lorem', isComplete: true},
{id: 2, text: 'Ipsum', isComplete: true},
{id: 3, text: 'Test', isComplete: false}
]);
return (
<div>
<h1>Todos</h1>
<p>My todos for today:</p>
<div id="incomplete">
{todos.filter(t => !t.isComplete).map(e => <Todo {...e} />)}
</div>
<p>Todos that've been already done:</p>
<div id="complete">
{todos.filter(t => t.isComplete).map(e => <Todo {...e} />)}
</div>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Based on OP's edit, you can introduce a function like getTodos
that will accept a boolean, and create the filter function based on that. Then return <Todo />
accordingly, this will reduce the duplicate filter()
, map()
and <Todo ... />
so you'll just need these 2 in the render
:
<div id="incomplete">
{getTodos()}
</div>
// ....
<div id="complete">
{getTodos(true)}
</div>
React Demo:
const { useState } = React;
const Todo = (props) => {
return <p>{props.text}</p>;
}
const Example = () => {
const [todos, setTodos] = useState([
{id: 1, text: 'Lorem', isComplete: true},
{id: 2, text: 'Ipsum', isComplete: true},
{id: 3, text: 'Test', isComplete: false}
]);
const getTodos = (complete) => {
const filterFunction = (complete) ? (t => t.isComplete) : (t => !t.isComplete);
return todos.filter(filterFunction).map(e => {
return <Todo {...e} />;
});
}
return (
<div>
<h1>Todos</h1>
<p>My todos for today:</p>
<div id="incomplete">
{getTodos()}
</div>
<p>Todos that've been already done:</p>
<div id="complete">
{getTodos(true)}
</div>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
CodePudding user response:
You can write an function to create the required jsx based on the task status use that for rendering the tasks please refer the code snippet below
const [todos, setTodos] = useState([
{ id: 1, text: "task1", isComplete: false },
{ id: 2, text: "task2", isComplete: false },
{ id: 3, text: "task3", isComplete: true }
]);
const tasks = () => {
let completeTasks = [];
let incompleteTasks = [];
todos.forEach((todo) => {
if (todo.isComplete) {
completeTasks.push(
<div className="task">
<p>{todo.text}</p>
</div>
);
} else {
incompleteTasks.push(
<div className="task">
<p>{todo.text}</p>
</div>
);
}
});
return { complete: completeTasks, incomplete: incompleteTasks };
};
const todoElements = tasks();
return (
<div className="App">
<h1>Todos</h1>
<div>
<p>My todos for today:</p>{" "}
<div id="incomplete">{todoElements.incomplete}</div>
<p>Todos that've been already done:</p>
<div id="complete">{todoElements.complete}</div>
</div>
</div>
);
please refer the sandbox : https://codesandbox.io/s/quizzical-leavitt-utt31m?file=/src/App.js:91-1107
CodePudding user response:
@0stone0 Thank you for an answer. Sorry, I haven't been precise. I've done this approach, but I wanted to make it more clear and don't repeat returning <Todo .../> component in two places.
Is it possible?
This is my code and it doesn't look very pretty tbh
<div className='tasks'>
<h2>Tasks</h2>
<div className="tasks-incomplete">
<h3>Zadania do wykonania</h3>
{incompleteTasks.length > 0 && incompleteTasks.map(task => (
<Task
key={task.id}
id={task.id}
text={task.text}
isComplete={task.isComplete}
dateStart={task.dateStart}
dateComplete={task.dateComplete}
handleDeleteTask={() => handleDeleteTask(task.id)}
handleClickEdit={()=>handleClickEdit(task.id, task.text)}
handleComplete={()=>handleComplete(task.id)}
/>
))}
{incompleteTasks.length === 0 && <p>brak zadań</p>}
</div>
{completeTasks.length > 0 && (
<div id="completed" className="tasks-complete">
<h3>Zadania wykonane</ h3>
{completeTasks.map( task => (
// todo: task do destrukturyzacji
<Task
key={task.id}
id={task.id}
text={task.text}
isComplete={task.isComplete}
dateStart={task.dateStart}
dateComplete={task.dateComplete}
handleDeleteTask={()=>handleDeleteTask(task.id)}
handleClickEdit={()=>handleClickEdit(task.id, task.text)}
handleComplete={()=>handleComplete(task.id)}
/>
))}
</div>
)}
</div>
)