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');
};
Related
I've been working with Nextjs for a while and I'm trying to avoid repetitive tasks. For a dynamic site, the menu and footer which are repeated on the whole page cause me a serious problem. To avoid this, I thought of making a Wrapper that I repeat on all the pages and pass the data in props.
But what I'm doing is making a wrapper that loads the data and won't be repeated on all the pages.
Look at the picture to get an idea of what I say
https://www.figma.com/file/u4okWHg3CpbgLPpZEiYJxI/Untitled?node-id=0%3A1
Layout and wrapper are two different things.
BaseLayout used to share a common section across multiple pages.
const BaseLayout= (props) => {
const {
className,user,navClass,loading,children,noSideBar,} = props;
// Define renderer to render the view
const renderer = () => (
<div className={`baselayout ${className}`}>
{noSideBar ? "" : <SideBar />}
<main>
{/* Prop drilling here */}
<Header className={navClass} user={user} loading={loading} />
{children}
<Footer />
</main>
</div>
);
return <> {renderer()} </>;
};
export default BaseLayout;
Then you use it inside a page
const AboutMe = () => {
return (
<BaseLayout user={data} loading={loading}>
{/* YOUR PAGE COMPONENT HERRE */}
</BaseLayout>
);
};
Wrappers are used to create and support basic UI elements based on the data or prop values. These wrapper contain an instance of another class or component and provide a new way to use an object. For example provide pages to an admin
function withAuth(Component) {
return (role) => {
return (props) => {
// an hook to get the props. based on this we decide what to render
const { data, loading } = useGetUser();
if (loading) {
return <p>Loading...</p>;
}
if (!data) {
return <Redirect to="/api/v1/login" />;
} else {
// if not authorized redirect to login
if (role && !isAuthorized(data, role)) {
return <Redirect to="/api/v1/login" />;
}
// if it is authorized return compoentn
return <Component user={data} loading={loading} {...props} />;
}
};
};
}
export default withAuth;
Then maybe we have a page for only admin
const OnlyAdmin = ({ user, loading }: { user: IUser; loading: true }) => {
return (
<BaseLayout user={user} loading={loading}>
<h1>I am Admin Page - Hello {user.name}</h1>
</BaseLayout>
);
};
// we are wrapping the component to pass a new property
export default withAuth(OnlyAdmin)("admin");
What I've done for that situation is have a context that gets the header and footer data client side and store it as JSON in window.sessionStorage. Then your header and footer are only loaded when blank or when invalidated by 'something' e.g certain routes or user login status changes.
Keeps things very fast and avoids needless delays. If you are going for full SSR you could do the same thing with server side sessions.
I'm using react-select for my web application
I have a dropdown with autofill, when I click on input I will see a list of all the car items that come from API but I need to enter the needed one and redirect to that details page.
Should I create route for this ?
Take a look:
When I click on BMW I need to go to bmw detials page.
if it was just simple car item I would create a button under and Link
<Link className='btn-item auction-btn mr-2' to={`/carDetails/${item.id}`}>Details</Link>.
with routing:
<Route
exact
path="/carDetails/:id"
render={({ match }) => (
<CarDetails item={data.find((item) => String(item.id) === String(match.params.id))} />
)}
/>
but this is react-select library and it's component that I have to filter and than to go to that page I need ..
You can do history.push in change handler of react-select. An example:
export default function MyComponent() {
const [value, setValue] = useState()
const history = useHistory()
function handleChange(newValue) {
setValue(newValue)
history.push(`/carDetails/${newValue.id}`)
}
return (
<Select
value={value}
options={options}
onChange={handleChange}
/>
)
}
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.
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.
Is it safe to use React.forwardRef method directly inside render function of another component -
Example -
function Link() {
// --- SOME EXTENSIVE LOGIC AND PROPS CREATING GOES HERE ---
// --- OMITTED FOR SIMPLICITY ---
// TO DO: Remove forward ref as soon Next.js bug will be fixed -
// https://github.com/zeit/next.js/issues/7915
// Please note that Next.js Link component uses ref only to prefetch link
// based on its availability in view via IntersectionObserver API -
// https://github.com/zeit/next.js/blob/canary/packages/next/client/link.tsx#L119
const TempShallow = React.forwardRef(props =>
cloneElement(child, {
...props,
...baseProps,
onClick: handleClick
})
);
return (
<NextLink href={href} as={as} prefetch={prefetch} passHref {...otherProps}>
<TempShallow />
</NextLink>
);
}
As you see it's a temporary workaround for a bug in Next.js v9 - https://github.com/zeit/next.js/issues/7915.
Beware forwardRef affects reconciliation: element is always re-created on parent re-rendering.
Say
function App() {
const [,setState] = useState(null);
const Input = React.forwardRef((props, ref) => <input {...props} />)
return (
<div className="App">
<h1>Input something into inputs and then click button causing re-rendering</h1>
<Input placeholder="forwardRef" />
<input placeholder="native" />
<button onClick={setState}>change state to re-render</button>
</div>
);
}
You may see that after clicking button forwardRef-ed input is dropped and re-created so it's value becomes empty.
Not sure if this could be important for <Link> but in general it means things you'd expect to run only once per life time(say fetching data in componentDidMount or useEffect(...,[]) as alternative) will happen much more frequently.
So if choosing between this side effect and mocking warning I'd rather ignore Warning. Or create own <Link > that will not cause warnings.
[UPD] missed one thing: React checks forwardRef by reference in this case. So if you make forwardRef out of the render(so it's referentially the same) it will not be recreated:
const Input = React.forwardRef((props, ref) => <input {...props} />)
function App() {
const [,setState] = useState(null);
return (
<div className="App">
<h1>Input something into inputs and then click button causing re-rendering</h1>
<Input placeholder="forwardRef" />
<input placeholder="native" />
<button onClick={setState}>change state to re-render</button>
</div>
);
}
But still I believe it's safer to ignore warning than to introduce such a workaround.
Code above has worse readability to me and is confusing("why ref is not processed at all? was it intentional? why this forwardRef is here and not in component's file?")
I concurr with skyboyer, I'll add that it might be possible to create the forwardRef component outside of the render function to avoid re-creating the component each render. To be checked.
const TempShallow = React.forwardRef(({ child, ...props }) => React.cloneElement(child, props))
function Link() {
// --- SOME EXTENSIVE LOGIC AND PROPS CREATING GOES HERE ---
// --- OMITTED FOR SIMPLICITY ---
// TO DO: Remove forward ref as soon Next.js bug will be fixed -
// https://github.com/zeit/next.js/issues/7915
// Please note that Next.js Link component uses ref only to prefetch link
// based on its availability in view via IntersectionObserver API -
// https://github.com/zeit/next.js/blob/canary/packages/next/client/link.tsx#L119
return (
<NextLink href={href} as={as} prefetch={prefetch} passHref {...otherProps}>
<TempShallow {...props} {...baseprops} child={child} onClick={onClick} />
</NextLink>
)
}