<div id="root">
#root {
display: flex;
flex-direction: column;
}
.spinner-container {
display: flex;
align-items: center;
justify-content: center;
}
.spinner {
color: #000;
display: inline-block;
width: 20px;
height: 20px;
animation: spinner .75s linear infinite;
border: 4px solid #000;
border-bottom-color: transparent;
border-radius: 100%;
background: 0 0;
}
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
const Spinner = () => {
return (
<div className="spinner-container">
<span className="spinner"/>
</div>
)
}
const TodosContext = React.createContext({
setTodos: (todos) => {},
setPage: (page) => {},
page: 1,
todos: []
});
const useTodosContext = () => {
return { page, todos } = React.useContext(TodosContext);
}
const useTodos = (defaultPage) => {
const { page, setTodos } = useTodosContext();
React.useEffect(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/');
const todos = await response.json();
const offset = (page - 1) * 20;
setTodos(todos.slice(offset, offset + 20));
}, [page]);
}
const TodosContainer = () => {
const [ state, setState ] = React.useState({
todos: [],
page: 1
});
const setTodos = (todos) => {
setState({state, todos});
}
const setPage = (page) => {
setState({state, page});
}
return (
<TodosContext.Provider value={{state, setTodos, setPage }}>
<>
<Todos/>
<TodosPaginate/>
</>
</TodosContext.Provider>
)
}
const Todos = () => {
const {page} = useTodosContext();
useTodos();
return todos.length > 0 ? <TodosList/> : <Spinner/>
}
const TodosList = () => {
const {todos} = useTodosContext();
return (
<ul>
{todos.map(todo => <TodoListItem todo={todo} key={`todo-${todo.id}`}/>)}
</ul>
)
}
const TodoListItem = ({todo}) => {
return <li id={`todo-item-${todo.id}`}>{todo.userId} - {todo.title} ({todo.completed ? "completed" : "in progress"})</li>
}
const TodosPaginate = () => {
const {page, setPage} = useTodosContext();
return (
<div>
<p>Page: {page}</p>
{ page > 1 ? <button onClick={() => setPage(page-1)}>Prev ({page-1})</button> : null }
{ page < 10 ? <button onClick={() => setPage(page+1)}>Next ({page+1})</button> : null }
</div>
);
}
ReactDOM.render(<TodosContainer/>, document.getElementById("root"));
View Compiled
This Pen doesn't use any external CSS resources.