API called twice while useEffect triggered once. ReactJS/Javascript - javascript

i am building a project, in which when user click the buyNow button in Basket (child 2) it will pass the props back to parent where it further pass it to another child in Signin(child 3) where we call an API call(inside useEffect) to update the mysql database but it seems that the API call is called twice as in database two records are being created and in front end i got two identical invoices record but different file names.
Any suggestion guys why i am facing this, please note if i remove the useEffect statement from the Signin it will then continuously non-stop call the API call so i think i cant remove the useEffect, other then this i cant see why it is happening. any suggestion please.
Main (APP)___|
|_Child 1(Home)
|_Child 2 (Basket)
|_Child 3 (signin)(API triggers here)---Sub Child 3-1 (useraccount)
Update-1: After removing the strictMode it does solve the issue, but does it means if i temporarily fixed the issue or if i have to use the stricMode and find the real problem
Child 2- Basket- Customer press the buyNow button and it triggers resetBasket function
const buyNow = (basketItems) => {
resetBasket(basketItems);
window.location.href = "http://localhost:3000/signin";
};
<ButtonGroup aria-label="quantityofproduct">
<Button variant="secondary" name="subtract" value="subtract" onClick={() => buyNow(basketItems)}>
Buy Now
</Button>
</ButtonGroup>
Main App resetBasket takes the basketitems and pass to parent element
const [finalBuy, setfinalBuy] = useState(finalbuyitems());
const resetBasket = (basketItems) => {
setfinalBuy(basketItems);
window.localStorage.setItem("user-final", JSON.stringify(basketItems));
}
<Route
path="/basket"
exact
render={(props) => (
<Basket {...props} userData={userData} userstatus={siginalready} basketItems={basketItems} updatedBasket={updatedBasket} resetBasket={resetBasket} />
)}
/>
<Route
path="/signin"
exact
render={(props) => <Signin {...props} buyNow={buyNow} resetBuynow={resetBuynow} userData={userData} finalBuy={finalBuy} userstatus={siginalready} />}
/>
Child 3 - Signin ,here we call the API call(using useEffect) and update the Mysql server and recieve the invoice in PDf format from backend
const [allInvoices, setallInvoices] = useState([]);
// The API call in the useEffect is triggering twice and thats why i am getting two invoices and two record at backend
useEffect(() => {
const headers = new Headers();
headers.append("content-type", "application/json");
const options = {
method: "POST",
headers,
credentials: "include",
body: JSON.stringify(),
};
const newRequest = new Request("http://localhost:5000/api/invoice-all", options);
(async () => {
const invoiceFetch = await fetch(newRequest)
.then((data) => {
return data.json();
})
.then((data1) => {
setallInvoices(data1);
})
.catch();
})();
// }, []);
return <div>{userstatus ? <Useraccount userinfo={userData} userstatus={userstatus} finalBuy={finalBuy} allInvoices={allInvoices} /> : <SigninOptions />}</div>;
Sub Child-Useraccount then it display the items recieved from the backend-mysql nodejs
// here the return is showing two different invoices of same items bought i.e two times the API is being called
return (
allInvoices.map((eachInvoice, index) => {
........................................})

I think the <React.StrictMode> is causing double execution. Please take a look after removing it from your top level component.
Please refer This Link for more details on StrictMode double execution.

Related

React table library that supports mutable data ( useRef )

Basically i'm looking for react table library that can take a mutable object ( to be specific an useRef object) as the main source of data to be displayed.
Basically i want to do something like this:
const TableViewComponent = () =>{
const tableData = useRef([{ rowName: 'test', value:0}] -> basically an array of objects that represents data for every row (the structure doesnt matter)
# code that receives data from server and updates the tableData.current with the data needed
return(
<Table data={tableData.current}/>
)
}
Basically, since i get a bunch of messages from the server and i update the data constantly (the number of rows stays the same), i don't want to rerender the table everytime. So i want to use the useRef to change the data thats being displayed on the table without triggering a rerender from react.
Im not sure if its something that can be done but any help is appreciated :). I tried react-table, rc-table but they didnt seem to work.
Basically, it looks to me like you'll have to do it yourself.
There's some libraries that might help you (like useTable which focuses on headless components) but I don't think they offer what you're looking for.
So let's quickly do a mock-up! (note: this is a quick sketch, assume that the undefined variables are stored somewhere else or are given from the fetch)
function useTableData({ initialData, itemsPerPage, ...etc }) {
const data = useRef(initialData);
const [loading, setLoading] = useState(false);
useEffect(() => {
data.current = fetchFromSomeWhere(...etc);
() => (data.current = null);
}, [etc /* Place other dependencies that invalidate out data here*/]);
const handleNewPage = useCallback(
async ({ pageIndex }) => {
if (!data.current[pageIndex * itemsPerPage]) {
setLoading(true);
data.current = [...data.current, await fetchMoreData(pageIndex)];
}
setLoading(false);
return data.current;
},
[itemsPerPage, data, setLoading],
);
return [data, handleNewPage, loading];
}
Notice how every single thing returned from this hook is a constant reference except for the loading! Meaning, that we can safely pass this to a memoized table, and it won't trigger any re-renders.
const TableContainer = memo(etc => {
const [data, handleNewPage, loading] = useDataForTable(...etc);
return (
<>
{loading && <Spinner />}
{/* Look ma, no expensive-renders! */}
<Body {...{ data }} />
<Pagination {...{ handleNewPage }} />
<OtherActions />
</>
);
});
However, you might have noticed that the body won't re-render when we click on the next page! Which was the whole point of pagination! So now we need some sort of state that'll force the Body to re-render
const TableContainer = memo(etc => {
const [currentPage, setPage] = useState(0);
const [data, handleNewPage, loading] = useDataForTable(...etc);
return (
<>
{loading && <Spinner />}
{/* We're still re-rendering here :( */}
<Body {...{ data, currentPage }} />
<Footer {...{ handleNewPage, setPage }} />
<OtherActions />
</>
);
});
And so we're left with a table that to use properly we need to:
Exercise restraint and properly invalidate old data. Otherwise, we'd be displaying incorrect data.
'Unpack' the data from the current ref on the Body component, and then render it.
In essence, after all that work we're still left with a solution isn't particularly attractive, unless you have some really complicated actions or some expensive scaffolding around the TableComponent itself. This might however be your case.

How to pass an instance to a function within a component in reactjs

If the terms used in question are incorrect please bare with me as this is the first time i'm using react js. I have written some code by referring lot of blogs, youtube, docs etc. and now i'm stuck since it is a mix of everything.
I have a requirement where i make a get call to an endpoint (about.js component) which returns json data which will be passed to (workspace.js component) where it is rendered and displayed. so far it is working fine.
next step, there is a link 'delete'(in cards element of workspace.js) on clicking, it should make a post call to an endpoint with the project_name. here i'm unable to make it work (confused with const, functions etc).
below is the code : (about.js)
import React, { useState, useEffect } from "react";
import Card from "react-bootstrap/Card";
import "./About.css";
import axios from "axios";
import Account from "./Workspace";
function About() {
const [resp_data, setdata] = useState("");
useEffect(() => {
const axios = require("axios");
axios({
method: "get",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
url: "http://127.0.0.1:8000/api/projects/",
})
.then(function (response) {
setdata(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {});
}, []);
if (resp_data != "") {
return (
<div>
<Account user={resp_data} />
</div>
);
} else {
return <h2>Loading...</h2>;
}
}
export default About;
workspace.js
import React, { Component } from "react";
import Card from "react-bootstrap/Card";
const Account = (props) => {
function handleClick(event) {
alert(event);
//need to get the project_name here
//make post call to an endpoint with project_name as data
}
const users = props.user.data;
return (
<div>
{users.map((user) => (
<div className="card-rows">
<Card className="card" key={user.Id}>
<Card.Body>
<Card.Title>
<b>Project : </b>
{user.project_name}
</Card.Title>
<Card.Subtitle className="mb-2 text-muted">
<b>DataSet : </b>
{user.dataset_name}
</Card.Subtitle>
<Card.Subtitle className="mb-2 text-muted">
<b>DataType : </b>
{user.data_type}
</Card.Subtitle>
<Card.Link
href="#"
name="hello"
className="delete"
onClick={this.handleClick({user.project_name})} // call handleclick and the projectname should be available within the function
>
Delete
</Card.Link>
<Card.Link href="/launch" className="launch">
Launch
</Card.Link>
</Card.Body>
</Card>
</div>
))}
</div>
);
};
export default Account;
it would be a great assistance if anyone could help
If you are in an functional component, you can get the user prop by adding curl braces. Also modify your onClick.
React onClick Event Handling
I would recommend you to go through React Documentation thoroughly if starting out.
This should work for you.
const Account = ({user}) => {
//use curly braces around props to fetch user prop
function handleClick(project_name) {
alert(project_name);
//need to get the project_name here
//make post call to an endpoint with project_name as data
}
return(
... //above code
<Card.Link
href="#"
name="hello"
className="delete"
onClick={() => handleClick(user.project_name)}
>
Delete
</Card.Link>
... //below
)
I believe the issue is how you are handing the onClick function in the Card.Link component.
OnClick functions take a function to handle the event. So right now you are just invoking a function but that wouldnt have any effect on the event that is implicitly being passed into the component.
More can be found here: https://reactjs.org/docs/handling-events.html
<Card.Link
href="#"
name="hello"
className="delete"
onClick={() => this.handleClick({user.project_name})} />
The problem in your implementation lies here
onClick={this.handleClick({user.project_name})}
because this would make the call while component gets rendered. But the react synthetic event handlers expect a function reference to be passed.
In order to achieve the same You can simply convert the onClick handler for Delete button
from
onClick={this.handleClick({user.project_name})}
to
onClick={() => this.handleClick(user.project_name)}
That would look like below
<Card.Link
href="#"
name="hello"
className="delete"
onClick={() =>
this.handleClick(user.project_name)}>
Delete
</Card.Link>
So, directly the handleClick method would receive the project_name.
const handleClick = project_name => {
console.log(projectName);
//You can use `project_name ` for making the API call.
}

Javascript React - not navigating to redirect page

I have been working on an inventory management system built in ReactJS with a back-end MariaDB database. The architecture includes user registration and logon modules with JWT authentication, a simple dashboard using D3, an inventory display screen using the React-Data-Table-Component, and an add inventory module. I will use the add-inventory module in the edit-inventory functionality by passing in a parameter to differentiate between add or edit mode (I have not yet implemented that parameter). A user can click on a row on the inventory display screen to pull that record up in edit mode, but currently I am not getting to the page to allow for inventory edit.
My code (inventorylist.component.jsx) looks like this:
updateRecord = row => {
this.setState({
...this.state,
selectedRow: row,
editSelectedRow: true
}, () => {
console.log('UPDATED STATE:',this.state.selectedRow)
})
}
editRecord = (event) => {
event.preventDefault();
return <Link to='/add' />
//return <Redirect to='/add' />
// return (
// <>
// <Router>
// <Switch>
// <Route path='/add' component={AddInventory} />
// </Switch>
// </Router>
// </>
// )
}
render() {
const inventoryTitle = this.props.jwt.username + "'s Inventory"
return (
<>
<DataTable
title={inventoryTitle}
striped={true}
highlightOnHover={true}
fixedHeader={true}
pagination={true}
paginationPerPage={10}
onRowClicked={this.updateRecord}
columns={columns}
data={this.state.inventory}
/>
<button type='button' onClick={this.editRecord}>Edit</button>
</>
)
}
This is not navigating to the add-inventory component. I have put a console.log into the editRecord function to verify that it is getting into that function after clicking on the button, but that's as far as it seems to be going.
I'm sure I'm just missing something fairly obvious here, but does anyone have any suggestions?
You can use history object to navigate imperatively:
history.push(<path>);
Read more about history
The problem is that you are return a Link which is a JSX element. What you need to do is one of the following:
If you want to keep the button element, you can push a new route into the history like this: history.push('/edit')
If you want to use Link, you need to replace you button with it and, when you click the link, it will automatically navigate there
With Hooks
If you were using hooks, you could use the useHistory hook provided by react-router to navigate to a different route doing something like this:
const history = useHistory();
const editRecord = () => {
history.push('/app');
};

Passing data from parent to child using react.State but State reset to initial state

I am facing an issue while passing the state to the child component, so basically I am getting customer info from child1(Home) and saving in the parent state(App) and it works fine.
And then I am passing the updated state(basketItems) to child2(Basket). But when I click on the Basket button the basket page doesn't show any info in console.log(basketItems) inside the basket page and the chrome browser(console) looks refreshed too.
Any suggestion why it is happening and how can I optimize to pass the data to child2(basket) from main (APP).
update:2
i have tired to simulated the code issue in sand box with the link below, really appreciate for any advise about my code in codesandbox (to make it better) as this is the first time i have used it
codesandbox
Update:1
i have made a small clip on youtube just to understand the issue i am facing
basketItems goes back to initial state
Main (APP)___|
|_Child 1(Home)
|_Child 2 (Basket)
Snippet from Parent main(App) component
function App() {
const [basketItems, setBasketItems] = useState([]);
const addBasketitems = (product, quantity) => {
setBasketItems(prevItems => [...prevItems, { ...product, quantity }])
}
console.log(basketItems) // here i can see the updated basketItems having customer data as expected [{...}]
return (
<Router>
<div className="App">
<header className="header">
<Nav userinfo={userData} userstatus={siginalready} />
</header>
<Switch>
<Route path="/" exact render={(props) => (
<Home {...props} userData={userData} userstatus={siginalready}
addBasketitems={addBasketitems}
/>
)}
/>
<Route path="/basket" exact render={(props) =>
(<Basket {...props} basketItems={basketItems} />
)}
/>
</Switch>
</div>
</Router>
Snippet from the child(basket)
function Basket({basketItems}) {
console.log(basketItems) // here i only get the [] and not the cusotmer data from parent component
return (
<div>
{`${basketItems}`} // here output is blank
</div>
);
}
export default Basket;
Snippet from the child(Home)
... here once the button is pressed it will pass the userselected details to parent
....
<Button name={producNumber} value={quantities[productName]} variant="primary"
onClick={() => {
addBasketitems(eachproduct, quantities[productName])
}}>
Add to Basket
</Button >
Your function works fine, the reason your output in addbasketItem does not change is the when using setState it takes some time to apply the changes and if you use code below you can see the result.
useEffect(()=>{
console.log('basket:',basketItems)
},[basketItems])
Your Basket component only renders once so replace it with this code and see if it works:
function Basket({ basketItems }) {
const [items, setItems] = useState([]);
useEffect(() => {
setItems(basketItems);
}, [basketItems]);
return <div>{`${items}`}</div>;
}
but for passing data between several components, I strongly suggest that you use provided it is much better.

Optimizing fetching api data with custom React hooks on searching by keyword change

I am new to React Hooks. I write a small code that displays a list of courses to the user. It contains 2 main components CourseList and Course and a custom hook useCourseList. Here is the code of the custom hook:
function useCourseList(searchString) {
const [courses, setCourses] = useState([]);
useEffect(() => {
function handleCourseListUpdating(nextCourses) {
setCourses(nextCourses);
}
getCourses(searchString, handleCourseListUpdating)
return () => {
setCourses([]);
}
}, [searchString]);
return courses;
}
The above code is fetching api data by a search string via getCourses function. Then it updates the retrieved data to courses state which is rendered by CourseList component. The useEffect function is triggered with searchString as its dependencies to keep updating courses' data once the search string changes.
The web browser refreshes many times corresponding with every single character change of the search string although the data results on those keyword changes are the same.
For example, we are looking for a game course with keyword "Game". Then it calls 4 times on useEffect one by one with "G", "Ga", "Gam", and "Game". Those keywords give the same results with my current api data but they are constantly updated React DOM via web interface. That leads to browser twinkling 4 times which is unnecessary. It also gives a bad user experience.
Is there any solution to prevent updating UI as data are still consistent? Is there any problem with the data flow?
For demonstration, you can see the full sample code here:
CodeSandbox
There are 2 changes you can make for a better ui experience
Debounce the API call for search
Do not set the state to empty in useEffect cleanup
You can choose to write your own custom debounce function or use it from a library such as lodash or underscore. Once you do that you can use useMemo to create a debounced instance of the getCourses function and call it inside useEffect
const debounce = (fn, delay) => {
let timer = null;
return function(...args) {
if (timer){
clearInterval(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay)
}
}
// A custom Hook
function useCourseList(searchString) {
const [courses, setCourses] = useState([]);
const getDebouncedCourses = useMemo(() => debounce(getCourses, 300), []);
useEffect(() => {
function handleCourseListUpdating(courses) {
setCourses(courses);
}
getDebouncedCourses(searchString, handleCourseListUpdating);
}, [searchString, getDebouncedCourses]);
return courses;
}
// CourseList component
function CourseList() {
const [searchString, setSearchString] = useState('');
const courses = useCourseList(searchString)
return (
<div>
{courses ? (
<div>
<TextField style={{ padding: 24 }}
id="searchInput"
placeholder="Search for Courses"
margin="normal"
onChange={event => setSearchString(event.target.value)}
/>
<Grid container spacing={4} style={{ padding: 24 }}>
{courses.map((course, index) => (
<Grid key={index} item xs={12} sm={6} lg={4} xl={3}>
<Course course={course} />
</Grid>
))}
</Grid>
</div>
) : "No courses found"}
</div>
);
}

Categories

Resources