How to onclick view product details using commercejs retrieve method - react-hooks

can you guys help?
I want to retrieve data from an array by using the products id.
<CardActionArea component={Link} value={product.id} to="/ViewProduct" onClick={() => getProductId(product.id)}>
<Product product={product} onAddToCart={onAddToCart} />
</CardActionArea>
const [productId, setProductId] = useState({});
const [details, setDetails] = useState([]);
const getProductId = (productId) => {
setProductId(productId)
}
console.log(productId)
const fetchDetails = async () => {
setDetails(await commerce.products.retrieve(productId));
}));
};
I always get this error with the same empty array.
enter image description here
[enter image description here][2]
and after I click view I get an empty array
[2]: https://i.stack.imgur.com/q5Adv.png

Related

How can I avoid re-renderging of child component using React.memo?

React.memo is not working as I expect.
Every time, I click the 'Button', the 'Oh No..' message is displayed.
I think displaying 'Oh No..' message means re-rendering of child component.
How do I modify my code to prevent re-rendering of child component caused by parent rendering?
import React from 'react';
function Test() {
const [page, setPage] = React.useState(0);
const Box = () => {
React.useEffect(() => console.info('Oh No..'), []);
return (<div>Box</div>)
};
const MemoBox = React.memo(Box);
return (
<div>
<MemoBox/>
<button onClick={()=>setPage(page+1)}>Button</button>
</div>
);
}
export default Test;
test image
I thought when I use React.memo, I can avoid re-rendering caused by parent re-redering.
I expected 'Oh No..' message is displayed once.
React.memo is not intended to be used as a "hook" inside of another component. Currently every time Test is rerendered - Box function is recreated and MemoBox variable is reasigned on each render. So every render - it is a "new" thing.
You can use React.useMemo or React.useCallback if you want to keep the declaration of Box and MemoBox inside Test component.
function Test() {
const [page, setPage] = React.useState(0);
const Box = () => {
React.useEffect(() => console.info("Oh No.."), []);
return <div>Box</div>;
};
// or or
const MemoBox2 = React.useMemo(() => Box, []);
const MemoBox = React.useCallback(Box, []);
useEffect(() => console.log(page), [page]);
return (
<div>
<MemoBox />
<button onClick={() => setPage(page + 1)}>Button</button>
</div>
);
}
If you want to use React.memo - then you need to move Box declaration and React.memo outside:
function Test() {
const [page, setPage] = React.useState(0);
useEffect(() => console.log(page), [page]);
return (
<div>
<MemoBox />
<button onClick={() => setPage(page + 1)}>Button</button>
</div>
);
}
const Box = () => {
React.useEffect(() => console.info("Oh No.."), []);
return <div>Box</div>;
};
const MemoBox = React.memo(Box);

Sending data to a server using remix run

I am having issues sending data to a server using remix run - I'm not sure I fully understand how useAction data works. I understand how the useLoaderData functions work but when you are trying to send data to a server I get errors.
What I want to do is send a post request to my server when I click a button - if I try and call create cart in the handleCLick event it says that createCart is not a function when it is
const submit = useSubmit()
function action({ request }) {
is this where i do my POST api call?
}
async function handleClick(event) {
await createCart(id, amount)
}
Cant seem to find any documentation which tells you how to do this?
EDIT: Hit send too early
With Remix, actions always run on the server. It is the method that Remix will call when you POST to a route.
// route.tsx
import { json, type ActionArgs, type LoaderArgs } from '#remix-run/node'
import { Form, useActionData, useLoaderData, useSubmit } from '#remix-run/react'
import { createCart } from '~/models/cart.server' // your app code
import { getUserId } from '~/models/user.server'
// loader is called on GET
export const loader = async ({request}: LoaderArgs) => {
// get current user id
const id = await getUserId(request)
// return
return json({ id })
}
// action is called on POST
export const action = async ({request}: ActionArgs) => {
// get the form data from the POST
const formData = await request.formData()
// get the values from form data converting types
const id = Number(formData.get('id'))
const amount = Number(formData.get('amount'))
// call function on back end to create cart
const cart = await createCart(id, amount)
// return the cart to the client
return json({ cart })
}
// this is your UI component
export default function Cart() {
// useLoaderData is simply returning the data from loader, it has already
// been fetched before component is rendered. It does NOT do the actual
// fetch, Remix fetches for you
const { id } = useLoaderData<typeof loader>()
// useActionData returns result from action (it's undefined until
// action has been called so guard against that for destructuring
const { cart } = useActionData<typeof action>() ?? {}
// Remix handles Form submit automatically so you don't really
// need the useSubmit hook
const submit = useSubmit()
const handleSubmit = (e) => {
submit(e.target.form)
}
return (
<Form method="post">
{/* hidden form field to pass back user id *}
<input type="hidden" name="id"/>
<input type="number" name="amount"/>
{/* Remix will automatically call submit when you click button *}
<button>Create Cart</button>
{/* show returned cart data from action */}
<pre>{JSON.stringify(cart, null, 2)}</pre>
</Form>
)
}

Is it possible to prevent `useLazyQuery` queries from being re-fetched on component state change / re-render?

Currently I have a useLazyQuery hook which is fired on a button press (part of a search form).
The hook behaves normally, and is only fired when the button is pressed. However, once I've fired it once, it's then fired every time the component re-renders (usually due to state changes).
So if I search once, then edit the search fields, the results appear immediately, and I don't have to click on the search button again.
Not the UI I want, and it causes an error if you delete the search text entirely (as it's trying to search with null as the variable), is there any way to prevent the useLazyQuery from being refetched on re-render?
This can be worked around using useQuery dependent on a 'searching' state which gets toggled on when I click on the button. However I'd rather see if I can avoid adding complexity to the component.
const AddCardSidebar = props => {
const [searching, toggleSearching] = useState(false);
const [searchParams, setSearchParams] = useState({
name: ''
});
const [searchResults, setSearchResults] = useState([]);
const [selectedCard, setSelectedCard] = useState();
const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, {
variables: { searchParams },
onCompleted() {
setSearchResults(searchCardsQueryResponse.data.searchCards.cards);
}
});
...
return (
<div>
<h1>AddCardSidebar</h1>
<div>
{searchResults.length !== 0 &&
searchResults.map(result => {
return (
<img
key={result.scryfall_id}
src={result.image_uris.small}
alt={result.name}
onClick={() => setSelectedCard(result.scryfall_id)}
/>
);
})}
</div>
<form>
...
<button type='button' onClick={() => searchCardsQuery()}>
Search
</button>
</form>
...
</div>
);
};
You don't have to use async with the apollo client (you can, it works). But if you want to use useLazyQuery you just have to pass variables on the onClick and not directly on the useLazyQuery call.
With the above example, the solution would be:
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDogPhoto] = useLazyQuery(GET_DOG_PHOTO, {
onCompleted: data => setDog(data.dog)
})
return (
<div>
{dog && <img src={dog.displayImage} />}
<button
onClick={() => getDogPhoto({ variables: { breed: 'bulldog' }})}
>
Click me!
</button>
</div>
);
}
The react-apollo documentation doesn't mention whether useLazyQuery should continue to fire the query when variables change, however they do suggest using the useApolloClient hook when you want to manually fire a query. They have an example which matches this use case (clicking a button fires the query).
function DelayedQuery() {
const [dog, setDog] = useState(null);
const client = useApolloClient();
return (
<div>
{dog && <img src={dog.displayImage} />}
<button
onClick={async () => {
const { data } = await client.query({
query: GET_DOG_PHOTO,
variables: { breed: 'bulldog' },
});
setDog(data.dog);
}}
>
Click me!
</button>
</div>
);
}
The Apollo Client documentation isn't explicit about this, but useLazyQuery, like useQuery, fetches from the cache first. If there is no change between queries, it will not refetch using a network call. In order to make a network call each time, you can change the fetchPolicy to network-only or cache-and-network depending on your use case (documentation link to the fetchPolicy options). So with a fetchPolicy change of network-only in your example, it'd look like this:
const AddCardSidebar = props => {
const [searching, toggleSearching] = useState(false);
const [searchParams, setSearchParams] = useState({
name: ''
});
const [searchResults, setSearchResults] = useState([]);
const [selectedCard, setSelectedCard] = useState();
const [searchCardsQuery, searchCardsQueryResponse] =
useLazyQuery(SEARCH_CARDS, {
variables: { searchParams },
fetchPolicy: 'network-only', //<-- only makes network requests
onCompleted() {
setSearchResults(searchCardsQueryResponse.data.searchCards.cards);
}
});
...
return (
<div>
<h1>AddCardSidebar</h1>
<div>
{searchResults.length !== 0 &&
searchResults.map(result => {
return (
<img
key={result.scryfall_id}
src={result.image_uris.small}
alt={result.name}
onClick={() => setSelectedCard(result.scryfall_id)}
/>
);
})}
</div>
<form>
...
<button type='button' onClick={() => searchCardsQuery()}>
Search
</button>
</form>
...
</div>
);
};
When using useLazyQuery, you can set nextFetchPolicy to "standby". This will prevent the query from firing again after the first fetch. For example, I'm using the hook inside of a Cart Context to fetch the cart from an E-Commerce Backend on initial load. After that, all the updates come from mutations of the cart.

MVC3 Text Box Text change Event

I have the following scenario, using mvc3:
I have a database table which holds a RecordID, RecordName and RecordType. Displayed are three text boxes, one for each of the fields mentioned previously.
My Question is, when i enter a RecordID into the relevant text box, i want to be able to show the RecordName and RecordType for that particular RecordID. How can i achieve this?
In View:
#Html.TextBoxFor(model => model.RecordId)
#Html.TextBoxFor(model => model.RecordName)
#Html.TextBoxFor(model => model.RecordType)
<script language="javascript">
$('#RecordId').change(function(){
var recordId = this.value;
$.getJSON("/MyController/GetRecordById",
{
id: recordId
},
function (data) {
$('RecordName').val(data.Name);
$('RecordType').val(data.Type);
});
});
</script>
In Controller:
public JsonResult GetRecordById(int id)
{
var record = recordRepository.GetById(id);
var result = new {
Name = record.Name,
Type = record.Type
}
return Json(result, JsonRequestBehavior.AllowGet);
}

call to controller to populate text box based on dropdownlistfor selection using Ajax

I have a dropdown and when I select an item from it, I want to pass on the selected value to a function in a controller, query the db and auto load a text box with query results.
How do I use Ajax to make that call to the controller when there is onclick() event on the dropdown?
My dropdown and textbox in my aspx page is:
<%: Html.DropDownListFor(model => model.ApplicationSegmentGuid, Model.ApplicationSegment)%>
<%: Html.TextAreaFor(model => model.EmailsSentTo, false, new { style = "width:500px; height:50px;" })%>
My function in controller is
public ActionResult AsyncFocalPoint(Nullable<Guid> ApplicationSegmentGuid)
{
string tempEmail = UnityHelper.Resolve<IUserDirectory>().EmailOf();
tempEmail = "subbulakshmi.kailasam#lyondellbasell.com" + tempEmail;
IList<string> EmailAddresses = new List<String>();
using (TSADRequestEntities context = UnityHelper.Resolve<TSADRequestEntities>())
{
EmailAddresses = context.FOCALPOINTs.Where(T => T.APPLICATIONSEGMENT.ItemGuid == ApplicationSegmentGuid && T.FlagActive)
.Select(T => T.Email).ToList();
}
foreach (string emailAddress in EmailAddresses)
tempEmail = tempEmail + ";" + emailAddress;
return Json(tempEmail, JsonRequestBehavior.AllowGet);
}
You could give your dropdown an id and url:
<%= Html.DropDownListFor(
model => model.ApplicationSegmentGuid,
Model.ApplicationSegment,
new { id = "myddl", data_url = Url.Action("AsyncFocalPoint") }
) %>
and then subscribe to the .change() event of the dropdown list unobtrusively and trigger the AJAX request:
$(function() {
$('#myddl').change(function() {
// get the selected value of the ddl
var value = $(this).val();
// get the url that the data-url attribute of the ddl
// is pointing to and which will be used to send the AJAX request to
var url = $(this).data('url');
$.ajax({
url: url,
type: 'POST',
data: { applicationSegmentGuid: value },
success: function(result) {
// TODO: do something with the result returned by the server here
// for example if you wanted to show the results in your textarea
// you could do this (it might be a good idea to override the id
// of the textarea as well the same way we did with the ddl):
$('#EmailsSentTo').val(result);
}
});
});
});

Resources