I have a problem, a bit hard to explain.
I use Hooks to set a state, from the data I receive from my API.
I display it on my frontend, no problem.
I would like to update ONE value, from ONE element.
Here is my full component :
import { UidContext } from "../components/AppContext"
import { totalIncome, totalFees, balance } from "./balance.utils"
import React, { useContext, useEffect, useState } from "react"
import axios from "axios"
export default function Balance() {
const uid = useContext(UidContext)
const [userWalletIncomes, setUserWalletIncomes] = useState('')
const [userWalletFees, setUserWalletFees] = useState('')
const [formIncomes, setformIncomes] = useState(false)
useEffect(() => {
if (uid !== null) {
axios.get(`${process.env.REACT_APP_API_URL}api/balance/${uid}`)
.then((res) => {
setUserWalletIncomes(res.data[0].incomes)
setUserWalletFees(res.data[0].fees)
})
}
}, [uid])
return (
<div className="h-screen">
<section className="border my-2 w-max md:w-11/12 mx-auto text-center">
<h1 className="text-3xl my-5">Revenus</h1>
{formIncomes === false && (
Object.entries(userWalletIncomes).map(([key, value]) => {
return (
<div className="text-left mx-auto flex" key={key}>
<p className="w-32 border p-1 capitalize">{key} :</p>
<p onClick={(e) => setformIncomes(true)} className="w-32 border p-1 text-green-500">{value}€</p>
</div>
)
})
)}
{formIncomes === true && (
Object.entries(userWalletIncomes).map(([key, value]) => {
return (
<div className="text-left mx-auto flex" key={key}>
<p className="w-32 border p-1">{key} :</p>
<input className="w-32 border p-1 text-green-500"
value={value}
onChange={(e) => setUserWalletIncomes(value)} />
</div>
)
})
)}
</section>
</div>
)
}
So, when I click on my <p>, it transforms to an input. Then I would like to modify the amount of income, and set it to the state.
For information, my collection (mongo) look like this :
{
"_id": {
"$oid": "60c763df3d260204865d2069"
},
"incomes": {
"salaire1": 1130,
"salaire2": 640,
"Prime": 90
},
"__v": 0,
"fees": {
"Box": 35,
}
}
So I need to update only the income category, and only the amount from the key selected. I don't know if I'm clear...
Any idea ?
Additional info : My setUserWalletIncomes in the input erase all the state, and replace it with the only value from the input.
You can take advantage of object destructuring to do that
setUserWalletIncomes({...userWalletIncomes, incomes: {...userWalletIncomes.incomes, [keySelected]: value}});
See the MDN docs for more info on the matter.
Edit: You already have it thanks to your Object.entries func. See below for full code.
<input className="w-32 border p-1 text-green-500"
value={value}
onChange={(e) => setUserWalletIncomes({...userWalletIncomes, incomes: {...userWalletIncomes.incomes, [key]: e.target.value}})} />
You just want only one map like this:
Object.entries(userWalletIncomes).map(([key, value]) => {
return (
<div className="text-left mx-auto flex" key={key}>
<p
className={`w-32 border p-1 ${
formIncomes === key ? "capitalize" : ""
}`}
>
{key} :
</p>
{formIncomes === key ? (
<input
className="w-32 border p-1 text-green-500"
value={value}
onChange={(e) =>
setUserWalletIncomes({
...userWalletIncomes,
[key]: e.target.value,
})
}
/>
) : (
<p
onClick={(e) => setformIncomes(key)}
className="w-32 border p-1 text-green-500"
>
{value}€
</p>
)}
</div>
);
});
Related
I am trying to make my list of stuff draggable, because it will be used to reorder a set of items retrieved from an API. All these items will have a prop named number, and with the drag and drop, I should change the order and the number in the visualization.
I followed along with some tutorials, the documentation, and many StackOverflow questions, but I have not been able to fix the problem. Each time I try to grab an element, in the console appears a warning message with the following content:
react_devtools_backend.js:4012
react-beautiful-dndUnable to find draggable with id: 1👷
This is a development-only message. It will be removed in production builds.
Here is the code I am working on:
Categories.tsx file with all the logic:
// Global imports
import {DragDropContext,Droppable, DropResult} from "react-beautiful-dnd";
// Local imports
// Context
import { menuContext } from "../menu";
// Components
import CategoryItem from "./cateogory";
// Types
import Category from "../../types/category";
function Categories(){
const {categories, setCategories} = useContext(menuContext);
/*functions and logic*/
const onDragEnd = (result:DropResult) => {
const {destination, source} = result;
if (!destination) return;
const items = Array.from(categories);
const [newOrder] = items.splice(source.index, 1);
items.splice(destination.index, 0, newOrder);
setCategories(items);
}
return (
<div className="greyBox">
<header className="header_menu">
<p>All your categories</p>
<div className="searchContainer">
<AiOutlineSearch className="searchIcon" />
<input type={"text"} placeholder={"Search"} className="searchBar" value={search}
onChange={(e) => handleModification(e)} />
</div>
</header>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="categories">
{(provided) =>
<div {...provided.droppableProps}
ref={provided.innerRef}
className="categoriesContainer" >
{filteredCategories.map((category: Category, idx: number) => {
return (
<CategoryItem key={category.id} idx={idx} category={category}/>
)
})}
</div>}
</Droppable>
</DragDropContext>
<button onClick={(e) => confirmChanges(e)} className="submitBTN bottomButton">Confirm changes</button>
</div>
)
}
export default Categories;
Category.tsx File into I have the single Item:
// Global imports
import React, { useContext, useState } from "react";
import { Draggable } from "react-beautiful-dnd";
// Local imports
// Context
import { menuContext } from "../menu";
// Types
import Category from "../../types/category";
function CategoryItem(props:{category:Category, idx:number}) {
const {categories, setCategories} = useContext(menuContext);
const {selectedCategory, setSelectedCategory} = useContext(menuContext);
/*Functions and various small things*/
let category = categories.find((category:Category) => category.id === props.category.id);
return (
<Draggable key={props.category.id} draggableId={props.category.id.toString()} index={props.idx}>
{(provided, snapshot) => (
<div className={selectedCategory === category.id ? "category activeCategory":"category"}
onClick={() => handleCategoryClick()}
style={{backgroundColor: category.isActive ? "" : "#FF7B7B"}}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div className="alwaysActiveCategory" style={{alignItems: props.category.id === selectedCategory ? "center" : "normal"}}>
<MdOutlineDragIndicator className="dragIcon" />
<p style={{display: props.category.id === selectedCategory ? "none" : "block" }}>{category.name}</p>
</div>
<aside style={{display: selectedCategory === category.id ? "flex": "none"}}>
<form className="modifyCategory" onClick={(e) => e.stopPropagation()} onSubmit={e => updateCategory(e)}>
<label htmlFor="categoryName">Category Name</label>
<p id="categoryProblem3">The name must be given</p>
<input value={name} onChange={(e) => changeName(e)} type="text" name="categoryName" id="categoryName" />
<label htmlFor="categoryDescription">Category Description</label>
<p id="categoryProblem4">The description must be given</p>
<textarea value={description} onChange={(e) => changeDescription(e)} name="categoryDescription" id="categoryDescription"
style={{maxWidth: "85%"}} />
<button>Update Category</button>
<div className="manageCategory">
<button onClick={(e) => hideCategory(e) }> <BiHide className="hideIcon"
style={{backgroundColor: categories.find((category: Category) => category.id === props.category.id).isActive ? "#FF7B7B" : "#76CB8E"}} />
{category.isActive ? "Hide Category" : "Show Category"}
</button>
<button onClick={(e) => deleteCategory(e)}> <BsTrash className="deleteIcon" /> Delete Category</button>
</div>
</form>
</aside>
</div>
)}
</Draggable>
)
}
export default CategoryItem;
They are pretty long, I tried to take away everything useless.
I don't know where the problem is in this script. It seems all fine to me, but there are some problems otherwise it wouldn't broke each time. If someone knows where my mistakes are, please tell me. Thank you a lot
I have a problem where I am trying to click on a certain div being returned by my backend and update it. I am trying to have it setup so that you click edit and the blogTitle and blogBody become input bars where you type in the new title and new body. In my code you can see an update that works and one that does not. The problem with the one that does not is that when I hit edit all blogBody's and blogTitle's become input bars instead of just one.
This 5 second of the issue video might make more sense than my explanation.
Now in my code this mainly deals with the following:
<div style={{display : 'flex', flexDirection: 'column-reverse'}}>
{ getBasic.miscData.map(({blogBody, blogTitle, _id}, i) => {
return(
<div key={i} >
<p>{i}</p>
{prompt ? <p>{blogBody}</p> : <input onChange={handleChange} name='newerValue' value={Item.newerValue} placeholder='new title'></input>}
{prompt ? <p>{blogTitle}</p> : <input onChange={handleChange} name='newerValue2' value={Item.newerValue2} placeholder='new title'></input>}
<p>{blogTitle}</p>
<p>{_id}</p>
<button onClick={deleteOne} value={_id}>Delete</button>
{prompt ? <button onClick={() => { setPrompt(!prompt)}} >Edit</button> : <button onClick={() => { setPrompt(!prompt)}} >Submit</button>}
</div>
Here is the full code:
import './App.css';
import axios from 'axios';
import React, { useState, useEffect } from 'react'
function App() {
const [getBasic, setGetBasic] = useState()
const [Item, setItem] = useState(
{
title: '',
description: '',
id: '',
newValue: '',
newValue2: '',
newerValue: '',
newerValue2: '',
previousValue: ''
}
)
const [prompt, setPrompt] = useState(true)
async function fetchData() {
await axios.get('http://localhost:3001/api')
.then(result => setGetBasic(result.data))
}
function handleChange(event) {
let {name, value} = event.target;
setItem(prevInput => {
return(
{
...prevInput,
[name]: value,
}
);
});
// console.log(Item);
}
//delete
function deleteOne(event) {
event.preventDefault();
const aValue = event.target.value;
const deleteItem = {
// id: Item.id
id: aValue
}
console.log(deleteItem)
axios.post('http://localhost:3001/api/delete', deleteItem);
window.location.reload();
// console.log(deleteItem)
}
function updateOne(event) {
event.preventDefault();
const updatedItem = {
previousValue: Item.previousValue,
newValue: Item.newValue,
newValue2: Item.newValue2
}
console.log(updatedItem)
axios.put('http://localhost:3001/api/update', updatedItem);
window.location.reload();
// console.log(deleteItem)
}
useEffect(() => {
fetchData()
}, [])
if (getBasic) {
return (
<div className="App">
<br />
{/* A version of update that works */}
<input onChange={handleChange} name="previousValue" value={Item.previousValue} placeholder="old value id" />
<input onChange={handleChange} name='newValue' value={Item.newValue} placeholder='new title'></input>
<input onChange={handleChange} name='newValue2' value={Item.newValue2} placeholder='new body'></input>
<button onClick={updateOne} >update</button>
{/* A version of update that does not work*/}
<div style={{display : 'flex', flexDirection: 'column-reverse'}}>
{ getBasic.miscData.map(({blogBody, blogTitle, _id}, i) => {
return(
<div key={i} >
<p>{i}</p>
{prompt ? <p>{blogBody}</p> : <input onChange={handleChange} name='newerValue' value={Item.newerValue} placeholder='new title'></input>}
{prompt ? <p>{blogTitle}</p> : <input onChange={handleChange} name='newerValue2' value={Item.newerValue2} placeholder='new title'></input>}
<p>{blogTitle}</p>
<p>{_id}</p>
<button onClick={deleteOne} value={_id}>Delete</button>
{prompt ? <button onClick={() => { setPrompt(!prompt)}} >Edit</button> : <button onClick={() => { setPrompt(!prompt)}} >Submit</button>}
</div>
)})}
</div>
</div>
)} else {
return (
<div>
<h1>Loading</h1>
</div>
)
};
}
export default App;
Now I understand why it is doing this, I just cannot think through the logic of getting my edit button to only edit one at a time. Also I understand the update will not work once I fix this, I am trying to solve this problem of editing 1 at a time first before setting it up how the other update one is.
Edit: so far I received one dislike and no comments I re read through this and realized it lacks a bit of focus thus made the code simpler:
import './App.css';
import axios from 'axios';
import React, { useState, useEffect } from 'react'
function App() {
const [getBasic, setGetBasic] = useState()
const [Item, setItem] = useState(
{
title: '',
description: '',
id: '',
newerValue: '',
newerValue2: '',
previousValue: ''
}
)
const [prompt, setPrompt] = useState(true)
async function fetchData() {
await axios.get('http://localhost:3001/api')
.then(result => setGetBasic(result.data))
console.log(getBasic)
}
useEffect(() => {
fetchData()
}, [])
if (getBasic) {
return (
<div className="App">
<div style={{display : 'flex', flexDirection: 'column-reverse'}}>
{ getBasic.miscData.map(({blogBody, blogTitle, _id}, i) => {
return(
<div key={i} >
<p>{i}</p>
{prompt ? <p>{blogBody}</p> : <input ></input>}
{prompt ? <p>{blogTitle}</p> : <input ></input>}
<p>{blogTitle}</p>
<p>{_id}</p>
{prompt ? <button onClick={() => { setPrompt(!prompt)}} >Edit</button> : <button onClick={() => { setPrompt(!prompt)}} >Submit</button>}
</div>
)})}
</div>
</div>
)} else {
return (
<div>
<h1>Loading</h1>
</div>
)
};
}
export default App;
This is all thats needed to work with to solve the issue at hand which is edit one at a time instead of all of them. Leaving the op for reference.
Have a component for each element so they each have their own unique prompt
For example,
//Element.js
import React from "react";
export default function Element({
i,
blogTitle,
handleChange,
_id,
deleteOne,
blogBody,
Item
}) {
const [prompt, setPrompt] = React.useState(true);
return (
<div>
<p>{i}</p>
{prompt ? (
<p>{blogBody}</p>
) : (
<input
onChange={handleChange}
name="newerValue"
value={Item.newerValue}
placeholder="new title"
></input>
)}
{prompt ? (
<p>{blogTitle}</p>
) : (
<input
onChange={handleChange}
name="newerValue2"
value={Item.newerValue2}
placeholder="new title"
></input>
)}
<p>{blogTitle}</p>
<p>{_id}</p>
<button onClick={deleteOne} value={_id}>
Delete
</button>
{prompt ? (
<button
onClick={() => {
setPrompt(!prompt);
}}
>
Edit
</button>
) : (
<button
onClick={() => {
setPrompt(!prompt);
}}
>
Submit
</button>
)}
</div>
);
}
//App.js
Now in the body return of your main file you could just do this
First import import Element from "./Element"; Then add the new component with specified props
{getBasic.miscData.map(({ blogBody, blogTitle, _id }, i) => {
return (
<Element
i={i}
blogTitle={blogTitle}
handleChange={handleChange}
_id={_id}
deleteOne={deleteOne}
blogBody={blogBody}
Item={Item}
/>
);
})}
I am building a simple todo-esk feature where if a user clicks the edit icon, only that item is editable. I implement this currently with a useState hook, const [editingMemberName, setEditingMemberName] = useState(false), but when I call a function, editMemberName all instances of items show an input field. This is not the experience I am going for.
Here are some screen shots that should make this more clear:
As you can see, I have two cards, but when I click the tool icon, both input boxes are displayed.
Here is the code:
const [editingMemberName, setEditingMemberName] = useState(false)
const [memberName, setMemberName] = useState('')
const handleChangeName = (e) => {
setMemberName(e.target.value)
}
// Update member name
const editMemberName = async (e) => {
setEditingMemberName(true)
}
const memberItems = members.map((member) => {
return (
<div
key={member.id}
>
<div className="flex items-center gap-4 w-full">
{editingMemberName ? (
<input
type="text"
placeholder="Johnny Appleseed"
onChange={handleChangeName}
/>
) : (
<>
<div>
{member.name}
</div>
<h3>{member.name}</h3>
</>
)}
</div>
<div>
{editingMemberName ? (
<button
onClick={() => updateMemberName(member.id)}
>
<CgCheckO size=".75em" />
</button>
) : (
<button
onClick={() => editMemberName(member.id)}
>
<FiTool size=".75em" />
</button>
)}
</div>
</div>
)
I've realized that editingMemberName hook operates on all instances, but I am not sure how to only target a single item.
Note: you can assume that the members array has a unique id for each item.
members: [
{
name: "Johnny",
id: 123
},
{
name: "George",
id: 456
}
]
That's because you are referring the boolean to all the boxes and not an individual element, use
const [editingMemberName, setEditingMemberName] = useState(members.map(e => false))
Something along the lines
const editMemberName = async (memberID, index) => {
let new_editing_members_state = members.map(e => false)
new_editing_members_state[index] = true
setEditingMemberName(new_editing_members_state)
}
const memberItems = members.map((member, index) => {
return (
<div
key={member.id}
>
<div className="flex items-center gap-4 w-full">
{editingMemberName ? (
<input
type="text"
placeholder="Johnny Appleseed"
onChange={handleChangeName}
/>
) : (
<>
<div>
{member.name}
</div>
<h3>{member.name}</h3>
</>
)}
</div>
<div>
{editingMemberName[index] ? (
<button
onClick={() => updateMemberName(member.id)}
>
<CgCheckO size=".75em" />
</button>
) : (
<button
onClick={() => editMemberName(member.id,index)}
>
<FiTool size=".75em" />
</button>
)}
</div>
</div>
)
I have a simple to do app in react every thing is okay when my app.js is a class component but when I changed it to a functional component occured an error = todos.filter is not a function
my files : Todo.js(functional) --> TodoList.js(functional) --> app.js(functional)
function TodoList(props) {
const [statusDone, setDone] = useState(false);
let { todos } = props;
console.log(todos);
let filterTodos = todos.filter((item) => item.done === statusDone);
return (
<>
<nav className="col-6 mb-3">
<div className="nav nav-tabs" id="nav-tab" role="tablist">
<a
className={`nav-item nav-link font-weight-bold ${
!statusDone ? "active" : ""
}`}
id="nav-home-tab"
onClick={() => setDone(false)}
>
undone{" "}
<span className="badge badge-secondary">
{todos.filter((item) => item.done === false).length}
</span>
</a>
<a
className={`nav-item nav-link font-weight-bold ${
statusDone ? "active" : ""
}`}
id="nav-profile-tab"
onClick={() => setDone(true)}
>
done{" "}
<span className="badge badge-success">
{todos.filter((item) => item.done === true).length}
</span>
</a>
</div>
</nav>
{filterTodos.length === 0 ? (
<p>there isn`t any todos</p>
) : (
filterTodos.map((item) => (
<Todo
key={item.key}
item={item}
delete={props.delete}
done={props.done}
edit={props.edit}
/>
))
)}
</>
);
}
main app class
function App() {
const [todos, settodos] = useState([]);
let addTo = (text) => {
settodos((prevState) => {
return {
todos: [prevState.todos, { key: Date.now(), done: false, text }],
};
});
};
return (
<div className="App">
<main>
<section className="jumbotron">
<div className="container d-flex flex-column align-items-center">
<h1 className="jumbotron-heading">Welcome!</h1>
<p className="lead text-muted">
To get started, add some items to your list:
</p>
<FormAddTodo add={addTo} />
</div>
</section>
<div className="todosList">
<div className="container">
<div className="d-flex flex-column align-items-center ">
<TodoList
todos={todos}
// delete={this.deleteTodo.bind(this)}
// done={this.toggleTodo.bind(this)}
// edit={this.editTodo.bind(this)}
/>
</div>
</div>
</div>
</main>
</div>
);
}
I've tried
let filterTodos =Object.values(todos).filter(item => item.done === statusDone)
and error fixed but my code dosen't work true
I hope u understand what I said :)
this functional component is for adding a todo
function FormAddTodo(props) {
const [text, setText] = useState("");
let formHandler = (e) => {
e.preventDefault();
props.add(text);
setText("");
};
let inputHandler = (e) => setText(e.target.value);
return (
<form className="form-inline mb-5" onSubmit={formHandler}>
<div className="row form-group">
<div className="col-8">
<input
type="text"
className=" form-control mx-sm-3"
placeholder="i want to do ..."
value={text}
onChange={inputHandler}
/>
</div>
<div className="col-4">
<button type="submit" className=" btn btn-primary">
add
</button>
</div>
</div>
</form>
);
}
The problem is on addTo function. You are not adding an element to todos array but you are setting todos as an object with a key called todos that contains an array. Try to modify addTo function in this way:
const addTo = (text) => {
let newElement = { key: Date.now(), done: false, text: text };
settodos(prevState => [...prevState, newElement]);
};
You have an error here:
function App() {
const [todos, settodos] = useState([]);
let addTo = (text) => {
settodos((prevState) => {
return {
todos: [prevState.todos, { key: Date.now(), done: false, text }],
};
});
};
your mutation funtion in settodos can try to concat prevState.todos with a new todo.
in fact with a useState setter, you get the value directly:
settodos(currentTodos => ...)
then return the value that you want (you return an object instead of an array)
also, if you want to concat two arrays, use a spread operator:
const newArray = [...someArray, newValue];
so to sum up, here's a fixed version of that piece of code:
function App() {
const [todos, settodos] = useState([]);
let addTo = (text) => {
settodos((prevTodos) => [
...prevTodos,
{ key: Date.now(), done: false, text }
]);
};
I am following this tutorial → https://blog.logrocket.com/pagination-in-graphql-with-prisma-the-right-way/
In the end, there is a Load More based pagination that looks like:
I tried implementing it like:
import React from 'react'
import { useQuery } from 'urql'
import { Card } from '../components/index'
import {
GET_ALL_ACQUISITIONS,
GET_ACQUISITIONS_BY_PRICE,
} from '../graphql/index'
export const AcquisitionList = ({
minPrice,
maxPrice,
undisclosed,
sortByDescPrice,
sortByAscStartupName,
}) => {
const [skip, setSkip] = React.useState(0)
const [result, reexecuteQuery] = useQuery({
query: GET_ACQUISITIONS_BY_PRICE,
variables: {
minPrice,
maxPrice,
undisclosed,
sortByDescPrice,
sortByAscStartupName,
skip,
take: 20,
},
})
const { data, fetching, error } = result
if (fetching) return <p className="mt-10 text-4xl text-center">Loading...</p>
if (error)
return (
<p className="mt-10 text-4xl text-center">Oh no... {error.message}</p>
)
return (
<>
<div className="flex flex-wrap justify-center mt-10">
{data.getAcquisitionsByPrice.map((startup, i) => {
return <Card key={i} startup={startup} index={i} />
})}
</div>
<div className="flex justify-center">
<button onClick={() => setSkip(skip + 20)}>Load More...</button>
</div>
</>
)
}
But I lose all the previous state when I click Load More... button. It also replaces the entire UI with Loading as my if (fetching) condition is on top of the display Cards.
How do I preserve the previous state while calling Prisma with the new query so I can show all the display Cards?
So the first time, I have 20 cards, 2nd time when I load it should have 40 cards & so on...
Currently, it only shows 20 cards at a time which is great if I had Previous & Next button but I want it to show it like Instagram with a click of a button.
Had to store the result in a separate local state and on every new query results, just had to add to it:
import React from 'react'
import { useQuery } from 'urql'
import { Card } from '../components/index'
import {
GET_ALL_ACQUISITIONS,
GET_ACQUISITIONS_BY_PRICE,
} from '../graphql/index'
const Template = ({ children }) => (
<p className="mt-10 text-4xl text-center">{children}</p>
)
export const AcquisitionList = ({
minPrice,
maxPrice,
undisclosed,
sortByDescPrice,
sortByAscStartupName,
}) => {
const [acq, setAcq] = React.useState([])
const [skip, setSkip] = React.useState(0)
const [result, reexecuteQuery] = useQuery({
query: GET_ACQUISITIONS_BY_PRICE,
variables: {
minPrice,
maxPrice,
undisclosed,
sortByDescPrice,
sortByAscStartupName,
skip,
take: 20,
},
})
const { data, fetching, error } = result
React.useEffect(() => {
setAcq([...acq, ...data])
}, [data])
if (fetching && !data) return <Template>Loading...</Template>
if (error && !data) return <Template>Oh no... {error.message}</Template>
return (
<>
{data.getAcquisitionsByPrice.length > 0 && (
<div className="flex flex-wrap justify-center mt-10">
{acq.length > 0 &&
acq.getAcquisitionsByPrice.map((startup, i) => {
return <Card key={i} startup={startup} index={i} />
})}
</div>
)}
{fetching && <Template>Loading...</Template>}
{error && <Template>Oh no... {error.message}</Template>}
{data.getAcquisitionsByPrice.length !== 0 && (
<div className="flex justify-center">
<button
className="inline-flex items-center px-4 py-2 mt-16 text-sm font-medium text-white border border-transparent rounded-md shadow-sm select-none transform hover:-translate-y-0.5 transition-all duration-150 bg-gradient-to-br from-indigo-600 hover:bg-gradient-to-br hover:from-indigo-700 focus:ring-indigo-500 focus:outline-none focus:ring-2 focus:ring-offset-2 hover:shadow-lg"
onClick={() => setSkip(skip + 20)}
>
<svg
className="w-6 h-6 text-white"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M15 13l-3 3m0 0l-3-3m3 3V8m0 13a9 9 0 110-18 9 9 0 010 18z"
></path>
</svg>
<span className="ml-2">Load More...</span>
</button>
</div>
)}
</>
)
}