I'm developing an app that can track pokemon stocks in the store. I fetch the name of data from pokeAPI. Each pokemon is supposed to have its own stock (my pokemon stock is still static) that can be updated in the next page when i click the pokemon's name.
This is the second page:
All in this page is still static. the only dinamic is the pokemons name that I get from params.
How can I update stock of each pokemon and save it? and show it in certain page? Using params (but i think it's not the best practice)? I read I can save the stock to localStorage and accessit later but where should I start from my code?
This is the modal that i will use to update my stock and pass the value to the second page.
first page:
export default function Homepage() {
const [pokemons, setPokemons] = useState([])
const [query, setQuery] = useState("")
const [search, setSearch] = useState("")
let navigate = useNavigate();
const getPokemons = async () => {
try {
let response = await axios.get(`${baseUrl}`)
let pokemons = await response.data.results
setPokemons(pokemons)
} catch (err) {
console.log(err.message)
}
}
const searchPokemon = async (pokemon, e) => {
console.log("masuk seacrh")
try {
if (e.key === 'Enter') {
let response = await axios.get(`${baseUrl} ${pokemon}`)
let data = await response.json()
setSearch(search)
}
} catch (err) {
console.log(err.message)
}
}
useEffect(() => {
getPokemons()
}, [])
// useEffect(() => {
// searchPokemon()
// }, [])
return (
<div className="app-container">
<h1 className="title">Stok Pokémon</h1>
<div className="">
<img src={searchIcon} className="search-icon" />
<input type="text"
className="search-box"
placeholder="Cari Pokémon"
onChange={(e) => setQuery(e.target.value)}
onKeyUp={searchPokemon} />
</div>
<div className="row">
<div className="col">
<Table className="table d-flex row">
<thead>
<tr className="d-flex justify-content-between th-border">
<th scope="col">Nama</th>
<th scope="col" className="d-flex text-right">Stok</th>
</tr>
</thead>
<tbody>
{
pokemons.filter(pokemon => pokemon.name.toLowerCase().includes(query)).map((pokemon, i) => {
console.log(pokemon)
return (
<tr className="d-flex justify-content-between">
<td key={i + 1} className="table-link" onClick={() => {
navigate(`/pokemon/${pokemon.name}`)
}} style={{ textTransform: 'capitalize' }}>{pokemon.name}</td>
<td className="pokemon-stock">10 pcs</td>
</tr>
)
})
}
</tbody>
</Table>
</div>
</div>
</div >
)
}
second page:
function UpdateStockModal(props) {
const [input, setInput] = useState({
pcs: 0,
lusin: 0
})
let navigate = useNavigate();
const [pcs, setPcs] = useState("")
const handleChange = e => {
let newValue = {
...input,
[e.target.name]: e.target.value,
};
setInput(newValue);
};
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
className="modal"
centered
>
<Modal.Header closeButton>
<Modal.Title className="modal-title">
Update stock
</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Masukkan jumlah stok yang tersedia di rak saat ini.</h4>
<Container>
<Row>
<Col>Kemasan</Col>
<Col>Jumlah</Col>
<Col>Stok</Col>
</Row>
<Row className="modal-table-body">
<Col >Pcs</Col>
<Col className="d-flex align-items-center">1 x <Form.Control className="modal-input pcs" type="text" name="pcs" value={input.pcs} onChange={handleChange} /> = </Col>
<Col>{input.pcs}</Col>
</Row>
<Row className="modal-table-body">
<Col>Lusin</Col>
<Col className="d-flex align-items-center">12 x <Form.Control name="lusin" className="modal-input lusin" type="text" value={input.lusin} onChange={handleChange} /> = </Col>
<Col>{12 * input.lusin}</Col>
</Row>
<Row className="modal-table-body">
<Col>Total Stok <span>(dalam pcs)</span></Col>
<Col>Lusin</Col>
<Col>{parseInt(12 * input.lusin) + parseInt(input.pcs)}</Col>
</Row>
</Container>
</Modal.Body>
<Modal.Footer>
<Button
variant="primary"
onClick={() => {
navigate(`/update-stock/`)
}} >
Simpan
</Button>
<Button
variant="secondary"
onClick={props.onHide}>
Batal
</Button>
</Modal.Footer>
</Modal>
);
}
export default function PokemonDetail() {
let navigate = useNavigate();
let { name } = useParams()
const [modalShow, setModalShow] = React.useState(false);
return (
<div className="pokemon-detail-page">
<div className="pokemon-detail_button-group">
<Button variant="outline-light" className="prev-button" onClick={() => {
navigate('/')
}}><img src={prevPageIcon}></img>Stok Pokémon</Button>
<Button className="update-stock-button" onClick={() => setModalShow(true)}>Update Stok</Button>
</div>
<p className="pokemon-detail-title" style={{ textTransform: 'capitalize' }}>{name}</p>
<div className="pokemon-detail-subtitle">
<p className="pokemon-detail-sub1">Sisa Stok</p>
<p className="pokemon-detail-sub2">10 pcs</p>
</div>
<div className="pokemon-detail-history">
<p className="pokemon-detail-history1">Riwayat Stok</p>
<p className="pokemon-detail-history2">Satuan stok dalam pcs</p>
</div>
<div>
<Table className='col-xs-12 mt-4' responsive>
<thead>
<tr className="th-border ">
<th scope="col">Waktu</th>
<th scope="col">Kegiatan</th>
<th scope="col">Catatan</th>
<th scope="col">Jumlah</th>
<th scope="col">Stok</th>
</tr>
</thead>
<tbody>
<tr className="align-items-center">
<td className="">2 Apr 2021, 08:00</td>
<td className="table-link">Update Stok</td>
<td className="">"Stok Awal"</td>
<td className="table-count-stock">+10</td>
<td className="table-bold">10</td>
</tr>
</tbody>
</Table>
</div>
<UpdateStockModal
show={modalShow}
onHide={() => setModalShow(false)}
/>
</div>
)
}
Sounds like a shopping cart. I'm assuming you'd like to update the stock for each pokemon. Then on another page, check all the updates before sending an update to the backend. For this I recommend redux saga or rtk query. The state will be globally accessible via the store so you can create your shopping cart securely without using local storage. You can google "redux saga shopping cart" for more examples but these are a couple I've found.
Redux saga
https://github.com/franklsm1/redux-saga-shopping-cart
RTK query
https://codesandbox.io/s/xed9r?file=/README.md
function UserListScreen() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.userList);
const { loading, error, users } = userList;
useEffect(() => {
dispatch(listUsers);
}, [dispatch]);
return (
<div>
<h1>Users</h1>
{loading ? (
<Loader />
) : error ? (
<Message variant="danger">{error}</Message>
) : (
<Table striped bordered hover responsive className="table-sm">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>EMAIL</th>
<th>ADMIN</th>
<th></th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user._id}>
<td>{user._id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td>
{user.isAdmin ? (
<i className="fas fa-check" style={{ color: "green" }}></i>
) : (
<i className="fas fa-check" style={{ color: "red" }}></i>
)}
</td>
</tr>
))}
</tbody>
</Table>
)}
</div>
);
}
export default UserListScreen;
all user list will show their id , name, and email but only show <h2> not showing data from the backend
TableItem component added without any data in UI. Could somebody help on this. On refereshing the UI, added data is shown with details in TableItem component.
Table Component Code
import TableItem from "./TableItem";
function Table({ searchWord }) {
const dispatch = useDispatch();
const dictData = useSelector((state) => state.dictionary);
useEffect(() => {
dispatch(getDictionaryAsync());
}, [dispatch]);
return (
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Word</th>
<th scope="col">Description</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{dictData &&
dictData
.filter((e) =>
searchWord === ""
? e
: e.word &&
e.word.toLowerCase().includes(searchWord.toLowerCase())
)
.map((item) => (
<TableItem item={item} key={item.id} searchWord={searchWord} />
))}
</tbody>
</table>
);
}
export default Table;
Below is the TableItem Component Code which i am trying to update,
When i add a word to dictionary it will fetch the details from the server and display it in the React app.
function TableItem({ item }) {
const [modal, setModal] = useState(false);
const openModal = () => {
setModal(true);
};
return (
<>
<tr key={item.id}>
<td style={{ textTransform: "capitalize" }}>{item.word}</td>
<td>
<b style={{ textTransform: "capitalize" }}>
{item.items && item.items[0].category} -{" "}
</b>
{item.items && truncate(item.items[0].definitions[0])}
</td>
<td>
<button className="btn btn-danger btn-sm " onClick={openModal}>
View
</button>
</td>
</tr>
<Modal isOpen={modal} ariaHideApp={true}>
<div className="modal-header">
<h3 className="modal-word-header">
{item.word && item.word.toUpperCase()}
</h3>
<button
className="btn btn-danger btn-sm"
onClick={() => setModal(false)}
>
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</div>
<div className="model-content">
<p>
{item.items &&
item.items.map((e) => {
return (
<>
<i>{e.category}</i>
<ul>
{e.definitions.map((def) => {
return <li>{def}</li>;
})}
</ul>
</>
);
})}
</p>
</div>
</Modal>
</>
);
}
Better add your TableItem component code!
Below code works fine and updated the UI on change in the Data in TableItem,
useEffect(() => {
dispatch(getDictionaryAsync());
}, [dispatch, dictData]); *<--updated code*
I am new to react,
i am trying to use async function but i have facing the following error
"Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead."
import {
Badge,
Card,
CardHeader,
CardFooter,
DropdownMenu,
DropdownItem,
UncontrolledDropdown,
DropdownToggle,
Media,
Pagination,
PaginationItem,
PaginationLink,
Table,
Container,
Row,
UncontrolledTooltip,
} from "reactstrap";
// core components
import Header from "components/Headers/Header.js";
import 'react-toastify/dist/ReactToastify.css';
import { Link } from "react-router-dom";
import axios from "axios";
import data from "./data";
var apitoken= localStorage.getItem('apitoken');
const api=axios.create({baseURL:"https://IP/DeAPI/api/v1/account"})
const options = {
headers: {'Authorization': apitoken}
}
const asynchronousFunction = async () => {
const response = await api.get("/",options)
window.input=response.data.response
}
const mainFunction = async () => {
const result = await asynchronousFunction()
return result
}
const state = { students:window.input}
//console.log(window.input);
const renderTableData=async ()=> {
console.log("test"+await mainFunction())
if(state.students) {
return state.students.map((student, index) => {
const { id, name, age, email } = student //destructuring
return (
<tr key={id}>
<th scope="row">
<Media className="align-items-center">
<a
className="avatar rounded-circle mr-3"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<img
alt="..."
src={
require("../../assets/img/theme/bootstrap.jpg")
.default
}
/>
</a>
<Media>
<span className="mb-0 text-sm">{id} </span>
</Media>
</Media>
</th>
<td>
<Badge color="" className="badge-dot mr-4">{name}</Badge>
</td>
<td>
<Badge color="" className="badge-dot mr-4">{age}</Badge>
</td>
<td>
<Badge color="" className="badge-dot mr-4">{email}</Badge>
</td>
</tr>
)
})
}
else
{
console.log("Something went wrong")
}
}
const Accounts = () => {
return (
<>
<Header />
{/* Page content */}
<Container className="mt--7" fluid>
{/* Table */}
<Row>
<div className="col">
<Card className="shadow">
<CardHeader className="border-0">
<h3 className="mb-0">All Account</h3>
</CardHeader>
<div>
</div>
<Table className="align-items-center table-flush" responsive>
<thead className="thead-light">
<tr>
<th scope="col">Account Name</th>
<th scope="col">Phone</th>
<th scope="col">Email</th>
<th scope="col">Account Owner</th>
{/* <th scope="col">Pincode</th> */}
<th scope="col" />
</tr>
</thead>
<tbody>
{renderTableData()}
</tbody>
</Table>
</Card>
</div>
</Row>
</Container>
</>
);
};
export default Accounts;
What have i done wrong here;
I am trying to get the student data from api and render it in the component dynamically.
The issue is with
<tbody>
{renderTableData()}
</tbody>
If i removed it its working,
Thanks in advance
You should not put anything except JSX into JSX. The problem is that async function returns Promise instead of JSX. You should rather use React.useEffect and React.useState to solve the loading problem. Let me show you.
Acutally another problem might be returning an array. Try wrapping it into <>...</> as I did.
import * as React from "react";
import {
Badge,
Card,
CardHeader,
CardFooter,
DropdownMenu,
DropdownItem,
UncontrolledDropdown,
DropdownToggle,
Media,
Pagination,
PaginationItem,
PaginationLink,
Table,
Container,
Row,
UncontrolledTooltip,
} from "reactstrap";
// core components
import Header from "components/Headers/Header.js";
import 'react-toastify/dist/ReactToastify.css';
import { Link } from "react-router-dom";
import axios from "axios";
import data from "./data";
var apitoken= localStorage.getItem('apitoken');
const api=axios.create({baseURL:"https://IP/DeAPI/api/v1/account"})
const options = {
headers: {'Authorization': apitoken}
}
const TableData = () => {
const [students, setStudents] = React.useState([]);
const [loading, setLoading] = React.useState(true);
React.useEffect(async () => {
const response = await api.get("/",options);
setStudents(response.data.response);
setLoading(false);
}, []);
if (loading) {
return <>Loading...</>;
}
return (
<>
{students.map((student, index) => {
const { id, name, age, email } = student //destructuring
return (
<tr key={id}>
<th scope="row">
<Media className="align-items-center">
<a
className="avatar rounded-circle mr-3"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<img
alt="..."
src={
require("../../assets/img/theme/bootstrap.jpg")
.default
}
/>
</a>
<Media>
<span className="mb-0 text-sm">{id} </span>
</Media>
</Media>
</th>
<td>
<Badge color="" className="badge-dot mr-4">{name}</Badge>
</td>
<td>
<Badge color="" className="badge-dot mr-4">{age}</Badge>
</td>
<td>
<Badge color="" className="badge-dot mr-4">{email}</Badge>
</td>
</tr>
)
})}
</>
)
}
const Accounts = () => {
return (
<>
<Header />
{/* Page content */}
<Container className="mt--7" fluid>
{/* Table */}
<Row>
<div className="col">
<Card className="shadow">
<CardHeader className="border-0">
<h3 className="mb-0">All Account</h3>
</CardHeader>
<div>
</div>
<Table className="align-items-center table-flush" responsive>
<thead className="thead-light">
<tr>
<th scope="col">Account Name</th>
<th scope="col">Phone</th>
<th scope="col">Email</th>
<th scope="col">Account Owner</th>
{/* <th scope="col">Pincode</th> */}
<th scope="col" />
</tr>
</thead>
<tbody>
<TableData />
</tbody>
</Table>
</Card>
</div>
</Row>
</Container>
</>
);
};
export default Accounts;
You should use the lifecycle hook useEffect to fetch async data (when working with functional components).
you also want to save your data in state. This is the default way to work with apis in react. Simply storing it in a variable will not work due to React's async behaviour.
Your error here is that you are trying to render a promise, which is not valid JSX
you can use the hook like this
const [state, setState] = useState({})
...
useEffect(() =>{
async(() =>{
const data = await mainFunction()
setState(data)
})()
}, []) //empty array means to only run once
<tbody>
{state.students?.map((student, index) => {
const { id, name, age, email } = student //destructuring
return (
<tr key={id}>
<th scope="row">
<Media className="align-items-center">
<a
className="avatar rounded-circle mr-3"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<img
alt="..."
src={
require("../../assets/img/theme/bootstrap.jpg")
.default
}
/>
</a>
<Media>
<span className="mb-0 text-sm">{id} </span>
</Media>
</Media>
</th>
<td>
<Badge color="" className="badge-dot mr-4">{name}</Badge>
</td>
<td>
<Badge color="" className="badge-dot mr-4">{age}</Badge>
</td>
<td>
<Badge color="" className="badge-dot mr-4">{email}</Badge>
</td>
</tr>
)
})
}
</tbody>
Try this one instead
I'm trying to confirm a user delete using Sweetalert2 in react, but when I use the 'Cancel' button, it still sends the form to delete the user, as if I've pressed 'Confirm'. Here's my code
export default function Users() {
const [users, setUsers] = useState([])
const [isModalOpen, setModal] = useState(false)
const [hover, setHover] = useState('')
const [userModal, setuserModal] = useState('')
const [show, setShow] = useState(false)
const [userDelete, setUserDelete] = useState('')
useEffect(() => {
fetch(`${OC_API}/users`)
.then(res => res.json())
.then(res => {
setUsers(res)
console.log(res)
})
.catch(e => console.log(`Hubo un error Cod. 200 ${e}`))
},[])
function toggleModal(user) {
setuserModal(user)
setModal(!isModalOpen)
}
function handleDeleteUser(user) {
fetch(`${OC_API}/userdelete`, {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"user_id": user._id})
}).then(res => {
if (res.ok){
alert('¡Usuario eliminado!')
} else {
alert('¡Hubo un error! Cod. 201')
}
}).then(() => {
fetch(`${OC_API}/users`)
.then(res => res.json(),
error => {alert(`Error de conexión ${error.message}`); throw error;})
.then(data => {
console.log(data)
setUsers(data)
})
.catch(e => console.log(`Hubo un error Cod. 202 ${e}`))
})
}
if (users == undefined || users == [] || users == null ){
return (
<Loading />
)
} else {
return(
<div className="container">
<motion.div className="row justify-content-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1}}
transition={{ duration: 1}}
style={{'paddingTop':'3em'}}
>
<div className="col col-md-12 text-right" >
<AddUserButton />
</div>
<div className="col col-md-12 text-center" >
<ParagraphStyle>
<div className="card" >
<div className="card-header text-left">
Administrar Usuarios
</div>
<div className="card-body">
<div className="table-responsive">
<AdminTableStyle>
<table className="table">
<thead >
<tr>
<th scope="col">Nombre</th>
<th scope="col">RUT</th>
<th scope="col">Correo</th>
<th scope="col">Rol</th>
<th scope="col">Acciones</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<Fragment key={user.id}>
<SweetAlert
show={show}
icon={'warning'}
title="Eliminar Usuario"
text={`¿Desea borrar a usuario ${userDelete.name}?`}
showCancelButton={true}
confirmButtonColor={'#3085d6'}
cancelButtonColor={'#d33'}
cancelButtonText={'Cancelar'}
confirmButtonText={'Confirmar'}
onCancel={ () => {setShow(false)
return }}
onConfirm={() => {
handleDeleteUser(userDelete)
setShow(false)
}}
/>
<tr onMouseOver={() => setHover(user._id)} onMouseLeave={() => setHover('')}>
<td style={{'textAlign': 'left'}}>{user.name}</td>
<td>{user.rut}</td>
<td>{user.email}</td>
<td>{user.role}</td>
<td>
<span style={{'marginRight': '5px'}}>
{
hover==user._id?
<a href="#" onClick={() => toggleModal(user)}>
<FontAwesomeIcon
icon='edit'
/>
</a>
:
''
}
</span>
<span>
{
hover==user._id?
<a href="#" onClick={() => {
setUserDelete(user)
setShow(true)
}}
>
<FontAwesomeIcon
icon='trash'
/>
</a>
:
''
}
</span>
</td>
</tr>
</Fragment>
))}
</tbody>
</table>
</AdminTableStyle>
</div>
</div>
</div>
</ParagraphStyle>
</div>
</motion.div>
<UserEdit
setuserModal={setuserModal}
isModalOpen={isModalOpen}
userModal={userModal}
toggleModal={toggleModal}
/>
</div>
)
}
}
I tried adding an onCancel option in the Sweetalert component and set the Show state to false, but it continues executing the handleDeleteUser function
Any suggestions?