I want to calculate totalSum. Api gives 'amount' rows, but I need to add them together and cant yet find correct function. I am still learning with reactjs.
const url = "http://127.0.0.1:8000/api/expense";
const TableCardList = () => {
const [data, setData] = useState([]);
const getData = async () => {
const response = await fetch(url);
const data = await response.json();
setData(data);
console.timeLog(data);
};
useEffect (() => {
getData();
}, []);
let tableList = data.map((row) => {
return (
<TableCard
key={row.id}
id={row.id}
created_at={row.created_at}
title={row.title}
category={row.category}
amount={row.amount}
/>
);
});
return(
<div className="row">
<table class="table">
{tableList}
<td><b className="number"> {totalSum} Eur</b></td>
</table>
</div>
);
};
export default TableCardList;
Array.prototype.reduce can do the trick
const url = "http://127.0.0.1:8000/api/expense";
const TableCardList = () => {
const [data, setData] = useState([]);
const [totalSum, setTotalSum] = useState(0);
useEffect(() => {
const getData = async () => {
const response = await fetch(url);
const data = await response.json();
setData(data);
console.timeLog(data);
};
getData()
}, []);
useEffect(() => {
const total = data.reduce((acc, row) => acc + row.amount, 0);
setTotalSum(total)
}, [data]);
let tableList = data.map((row) => (
<TableCard
key={row.id}
id={row.id}
created_at={row.created_at}
title={row.title}
category={row.category}
amount={row.amount}
/>
));
return (
<div className="row">
<table class="table">
{tableList}
<td><b className="number"> {totalSum} Eur</b></td>
</table>
</div>
);
};
export default TableCardList;
Related
this is a component that retrieve data from API and generate a table. One button in a column for removing the row opens a Modal.
For avoiding in first component render to trigger api.delete request, an useRef set as false in second useEffect
Modal's Delete button return row info in deleteHandler function which successfully trigger api.delete request on confirmation, remove user in backend however table is not reloading.
Once row is removed expected result is triggering api.get request and display table now updated and without row removed.
In order to get that result I tried with const [reload, setReload] = useState(false); state which introduce another dependency to both userEffect
reload state effectively reload table data updated however it cause also that api.delete request trigger with const ida undefined. Below component script it can find useEffect with my tried.
Any hint or possible solution is appreciated
import React, { Fragment, useEffect, useState, useRef } from "react";
... other imports ...
export default function AllUsers() {
const api = useApi();
const [userData, setUserData] = useState();
const [showModal, setShowModal] = useState(false);
const navigate = useNavigate();
const [modalMessage, setModalMessage] = useState();
const [removeUserId, setRemoveUserId] = useState();
const [ida, setIda] = useState();
let effectRan = useRef(false);
const [reload, setReload] = useState(false);
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
} else {
setUserData(null);
}
})();
}, [api]);
useEffect(() => {
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
} else {
console.log(response.body.errors);
}
})();
}
return () => (effectRan.current = true);
}, [api, ida]);
const handleEditClick = (e, rowIndex) => {
// console.log("user/" + rowIndex.username);
navigate("/user/" + rowIndex.username, [navigate]);
};
const handleRemoveClick = (e, rowIndex) => {
// console.log([rowIndex]);
setShowModal(true);
setModalMessage({
title: `Remove ${rowIndex.username}`,
message: `User's email to remove ${rowIndex.email}`,
});
setRemoveUserId(rowIndex.id);
};
const closeHandler = () => {
setShowModal(false);
};
const deleteHandler = () => {
// console.log(removeUserId);
setIda(removeUserId);
setShowModal(false);
};
// console.log(ida, idb);
return (
<Fragment>
{showModal && (
<BtModal
show={showModal}
title={modalMessage.title}
message={modalMessage.message}
handleClose={closeHandler}
onConfirm={deleteHandler}
/>
)}
<Body>
<h1>User Table</h1>
{userData === undefined ? (
<Spinner animation="border" />
) : (
<>
{userData === null ? (
<p>Could not retrieve users.</p>
) : userData.length === 0 ? (
<p>There are not users in system</p>
) : (
<UserListTable
newData={userData}
handleEditClick={handleEditClick}
handleRemoveClick={handleRemoveClick}
/>
)}
</>
)}
</Body>
</Fragment>
);
}
useEffect updated with reload state:
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
effectRan.current = false;
} else {
setUserData(null);
}
})();
}, [api, reload]);
useEffect(() => {
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
setReload(!reload);
} else {
console.log(response.body.errors);
}
})();
}
return () => (effectRan.current = true);
}, [api, ida, reload]);
Here you modify your last edit
const deleteHandler = () => {
// console.log(removeUserId);
setIda(removeUserId);
setReload(!reload)
setShowModal(false);
};
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
} else {
setUserData(null);
}
})();
}, [api]);
useEffect(() => {
effectRan.current = reload;
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
setReload(!reload);
} else {
console.log(response.body.errors);
}
})();
}
return () => {
effectRan.current = false;
};
}, [api, ida, reload]);
It looks like you're trying to delete an item in the table and confirm it via a prompt. I can see that you've used a lot of useEffects, which might be making things a bit complicated.
Don't worry though, I have a solution that should be much simpler. Let me show you what I mean!
const Page = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState([]);
const [deleting, setDeleting] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const deleteHandler = async() => {
setDeleting(true);
await api.delete(selectedItem);
// Next you can either update the state itself
let tD = [...data];
const index = tD.findIndex((i) => i.id == selectedItem);
tD.splice(index, 1);
setSelectedItem(tD);
// or refetch data from the backend and update the state
const d = await api.getData();
setData(d);
setDeleting(false);
setSelectedItem(null);
};
useEffect(() => {
const fetchData = async() => {
setLoading(true);
const d = await api.getData();
setData(d);
setLoading(false);
}
fetchData();
}, [])
return loading ? <div>Loading...</div> : <>
{/*Other JSX elements*/}
{selectedItem && <div>
{/*prompt body*/}
<button onClick={() => {setSelectedItem(null)}}>Cancel</button>
<button disabled={deleting} onClick={() => {deleteHandler()}}>Delete</button>
{/*Button will be disabled when the DELETE request is sent*/}
</div>}
{data.map((row) => {
return <tr key={row.id}>
<td>...</td>
<td>...</td>
<td>
<button onClick={setSelectedItem(row.id)}>Delete</button>
</td>
</tr>
})}
</>
}
I'm working on implementing a braintree payment method in my react/mui app. I've found a way that works, but it's in a class component. How can I convert this info a proper functional component?
const BraintreeDropInPaymentMethod = () => {
class Store extends React.Component {
instance;
state = {
clientToken: '<BRAIN TREE KEY>'
};
async componentDidMount() {
const response = await fetch("server.test/client_token");
const clientToken = await response.json();
this.setState({
clientToken,
});
}
async buy() {
const { nonce } = await this.instance.requestPaymentMethod();
await fetch(`server.test/purchase/${nonce}`);
}
render() {
if (!this.state.clientToken) {
return (
<div>
<h1>Loading...</h1>
</div>
);
} else {
return (
<div>
<DropIn
options={{ authorization: this.state.clientToken }}
onInstance={(instance) => (this.instance = instance)}
/>
<Button
variant='contained'
onClick={this.buy.bind(this)}
>
Create Account
</Button>
<Button
variant='outlined'
sx={{ marginLeft: 3 }}
color='warning'
onClick={(e) => handleCancelAccountCreation(e)}
href='/store-front'
>
Cancel
</Button>
</div>
);
}
}
}
const [user, setUser] = useState({})
const handleCancelAccountCreation = (event) => {
setUser({})
document.getElementById('signInBtn').hidden = false
}
return (
<Store/>
)
}
this is my attempt, but I'm coming up short on how I should handle componentDidMount(). I know how to handle useState in some situations, except for this one. Also, how can I handle the 'instance' section in a functional format? thanks.
const BraintreeDropInPaymentMethod = () => {
const [token, setToken] = useState('<BRAIN TREE KEY>')
const [user, setUser] = useState({})
const contactServer = async () => {
const res = await fetch('server.test/client_token')
const clientToken = await res.json()
console.log(clientToken)
setToken(token)
}
const buy = async () => {
const { nonce } = await this.instance.requestPaymentMethod()
await fetch(`server.test/purchase/${nonce}`)
}
const handleCancelAccountCreation = (event) => {
setUser({})
document.getElementById('signInBtn').hidden = false
}
const createAccountOptions = () => {
if (!token) {
return (
<div>
<h1>Loading...</h1>
</div>
) else {
return (
<div>
<DropIn
options={ authorization: {setToken})
onInstance={(instance) => (this.instance = instance)}
/>
<Button
variant="contained'
onClick={buy}
>
Create Account
</Button
variant='outlined'
sx={{ marginLeft: 3 }}
color='warning'
onClick={(e) => handleCancelAccountCreation(e)}
href='/store-front'
>
<Button>
Cancel
</Button>
</div>
)
}
}
}
return(
<>
<createAccountOptions/>
</>
)
}
The functional equivalent of componentDidMount() is the useEffect hook.
In this case you would change this:
async componentDidMount() {
const response = await fetch("server.test/client_token");
const clientToken = await response.json();
this.setState({
clientToken,
});
}
Into something like this:
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch("server.test/client_token");
const clientToken = await response.json();
setState((old) => clientToken);
};
Using the useEffect hook with an empty array as a dependency makes the function in it only run once as the component mounts.
Why do I have undefined in selectedUser?
After all, I go through the find method through the users array and the first id of the users array should be written to selectedUser
function App() {
const [selectedUserId, setSelectedUserId] = useState(null)
const [users,setUsers] = useState([])
const selectedUser = users.find(u => u.id === selectedUserId)
console.log(users)
console.log(selectedUser)
useAsyncEffect(async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users')
const data = await res.json()
setUsers(data)
}, [])
const onUserClick = (userId) => {
setSelectedUserId(userId)
}
return (
<div>
{ selectedUser ? <ListUsers users={users} onUserClick={onUserClick} /> : <Profile user=
{selectedUser} />
}
</div>
)
}
You can use a loading state to determine when the async function is done and only then you can set the selected user. Otherwise, it remains set to 'null' when the 'return' is rendered.
Below is pseudo code.
function App() {
const [selectedUserId, setSelectedUserId] = useState(null)
const [users,setUsers] = useState([])
const selectedUser = users.find(u => u.id === selectedUserId)
const [loading, setLoading] = useState(false)
console.log(users)
console.log(selectedUser)
useAsyncEffect(async () => {
setLoading(true)
const res = await fetch('https://jsonplaceholder.typicode.com/users')
const data = await res.json()
setUsers(data)
setLoading(false)
}, [])
const onUserClick = (userId) => {
setSelectedUserId(userId)
}
if (loading){
return "Loading..."
// you can use a nicer loader component here
}
return (
<div>
{ selectedUser ? <ListUsers users={users} onUserClick={onUserClick} /> : <Profile user=
{selectedUser} />
}
</div>
)
}
I have created a table in which I would now like to be able to delete individual rows. The button is displayed and can be clicked, but the deleteItmen function is not called. Unfortunately I'm quite new to React.js and haven't found a solution yet.
const Tab = (props) => {
const [showContent, setShowContent] = useState([]);
const [loading, setLoading] = useState(false);
const token = getUser.getCurrentUser().token;
useEffect(() => {
LoadTable();
// eslint-disable-next-line
}, []);
const LoadTable = () => {
if (props.table === undefined)
getData("device", setShowContent, setLoading);
else getData(props.table, setShowContent, setLoading);
};
const getHeader = () => {
return <tr>{showContent[0].map((elem, index) => <th>{elem}</th>)}</tr>
}
const getBody = () => {
const bodylist = Object.values(showContent.slice(1));
const body = bodylist.map((elem, index) => ({ ...elem, button: (<button onclick={() => deleteItem(elem.id)}>Delete</button>) }))
return body.map((row, index) => <tr> {Object.values(row).map((elem, i) => <td> {elem}</td>)} </tr>)
}
const deleteItem = async (id) => {
await axios.delete('http://localhost:3000/api/' + id, { tablename: props.table }, {
headers: {
"x-access-token": token,
},
})
LoadTable();
}
return (
<div>
{!loading ?
<h1>Loading</h1> :
<table>
<thead>{getHeader()}</thead>
<tbody>{getBody()}</tbody>
</table>
}
</div>
);
rnmservice.js
export function getrnm({ url }) {
return new Promise((resolve, reject) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data);
});
});
}
export async function getAllrnm(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data);
});
});
}
app.js
import React, { useState, useEffect } from "react";
import Navbar from "./components/Navbar";
import Card from "./components/Card/Card";
import { getrnm, getAllrnm } from "./services/rmservice";
function App() {
const [rnmData, setRnmData] = useState([]);
const [nextUrl, setNextUrl] = useState("");
const [prevUrl, setPrevUrl] = useState("");
const [loading, setLoading] = useState(true);
const initialURL = "https://rickandmortyapi.com/api/episode/";
useEffect(() => {
async function fetchData() {
let response = await getAllrnm(initialURL);
setNextUrl(response.next);
setPrevUrl(response.previous);
await loadRnm(response.results);
setLoading(false);
}
fetchData();
}, []);
const next = async () => {
setLoading(true);
let data = await getAllrnm(nextUrl);
await loadRnm(data.results);
setNextUrl(data.next);
setPrevUrl(data.previous);
setLoading(false);
};
const prev = async () => {
if (!prevUrl) return;
setLoading(true);
let data = await getAllrnm(prevUrl);
await loadRnm(data.results);
setNextUrl(data.next);
setPrevUrl(data.previous);
setLoading(false);
};
const loadRnm = async data => {
let _rnmData = await Promise.all(
data.map(async rnm => {
let rnmRecord = await getrnm(rnm);
return rnmRecord;
})
);
setRnmData(_rnmData);
};
return (
<>
<Navbar />
<div>
{loading ? (
<h1 style={{ textAlign: "center" }}>Loading...</h1>
) : (
<>
<div className="btn">
<button onClick={prev}>Prev</button>
<button onClick={next}>Next</button>
</div>
<div className="grid-container">
{rnmData.map((rnm, i) => {
return <Card key={i} ricmor={rnm} />;
})}
</div>
<div className="btn">
<button onClick={prev}>Prev</button>
<button onClick={next}>Next</button>
</div>
</>
)}
</div>
</>
);
}
export default App;
Here My sandbox link is https://codesandbox.io/s/musing-thunder-frsk6 . I'm trying to fetch data and to do navigation using the API of ricky and morty (https://rickandmortyapi.com/api/episode/) but when I click next button I'm getting error as "SyntaxError Unexpected token < in JSON at position 0"
You just replace your useEffect with this
useEffect(() => {
async function fetchData() {
let response = await getAllrnm(initialURL);
console.log(response.info);
setNextUrl(response.info.next);
setPrevUrl(response.info.previous);
setPages(response.info.pages);
await loadRnm(response.results);
setLoading(false);
}
fetchData();
}, []);
you are directly accessing next and previous. access response.info.next like this because all next/previous coming in info.