Configure a Widget to use REST Endpoints
Once you have created a custom widget, you can update your application to communicate with the REST API.
To continue creating the widget code that displays a list of currencies,
you must obtain the location from which the data should be retrieved.
For this example, assume that you get the data from the Redux store.
First, you must declare the component. Do this by exporting a connect component. It acts as a wrapper around your component
and lets you connect your component to a specific part of the state
located in the Redux store. This allows properties to be passed in
as props to your component. Then you pass a selector
that points to a specific part of the Redux store into the constructor
for that component. Whenever those properties change, your component
will re-render. The connect component binds the display
of your component to the values of your properties.
This is how your component obtains data. You can also use the connect component to perform specific tasks, which might
result in updates to the data in the Redux store. This would result
in re-rendering certain parts of your application.
/plugin/components/currency-selector/index.js file where the wrapped component is identified:/**
* Wrap the component with a "connect" object that supplies the state's
* currency information as props and redisplays the component when any of those
* props change.
*/
export default connect(getCurrencyInfo)(CurrencySelector);When you create a widget that may have significant differences between user interfaces, it is recommended that you create separate widgets.
In the following code, the HTML looks up the currencies array.
Then it takes the given currency and adds options using the displayName of the given currency. This is defined in the /plugin/components/currency-selector/index.js file.
index.js file for the currency selector should
resemble:import React, {useContext} from 'react';
import {StoreContext} from '@oracle-retail-digital-commerce/react-ui/contexts';
import Styled from '@oracle-retail-digital-commerce/react-components/styled';
import {connect} from '@oracle-retail-digital-commerce/react-components/provider';
import css from './styles.css';
import {getCurrencyInfo} from '../../selectors';
import {listCurrenciesFetcher} from '../../fetchers';
import {useListCurrenciesFetcher} from '../../fetchers/hooks';
// The server-side rendering framework checks each component for a "fetchers" array
// to determine what actions to take in order to populate the initial state. For this
// component, we need the currency list.
export const fetchers = [listCurrenciesFetcher];
/**
* The CurrencySelector component.
*
* @param {object} props.currencies Array of info about available currencies
* @param {object} props.selectedCurrency Info about the selected currency
* @param {string} props.label* Resource strings
*/
const CurrencySelector = ({
currencies,
selectedCurrency,
labelCurrencies,
labelSelectACurrency,
labelSelectedCurrency
}) => {
// Make sure we have the latest currency list during client-side rendering.
const store = useContext(StoreContext);
useListCurrenciesFetcher(store);
// Invoked when the user selects a currency from the
// select element. Invokes the '_getCurrency' action to
// update the selected currency with info from the server.
const onCurrencyChange = event => {
const repositoryId = event.target.value;
if (repositoryId) {
store.action('_getCurrency', {repositoryId});
}
};
// Display a panel with:
// * a select element with a placeholder and all available currencies
// * a panel with information about the selected currency
return (
<Styled id="CurrencySelector" css={css}>
<div className="CurrencySelector">
{currencies && (
<div>
<span className="CurrencySelector__Label">{labelCurrencies}</span>
<select
value={selectedCurrency && selectedCurrency.repositoryId}
onChange={onCurrencyChange}
onBlur={onCurrencyChange}
>
<option value="">{labelSelectACurrency}</option>
{currencies.map(currency => (
<option key={currency.repositoryId} value={currency.repositoryId}>
{currency.displayName}
</option>
))}
</select>
</div>
)}
{selectedCurrency && (
<div className="CurrencySelector__SelectedCurrencyInfo">
{labelSelectedCurrency} {selectedCurrency.displayName} ({selectedCurrency.currencyCode})
</div>
)}
</div>
</Styled>
);
};
/**
* Wrap the component with a "connect" object that supplies the state's
* currency info as props and redisplays the component when any of those
* props change.
*/
export default connect(getCurrencyInfo)(CurrencySelector);The select attribute has an onChange handler, and a function that is defined named onCurrencyChange. The value={currency.repositoryId} uses the ID
of the currency as the value of the option. If the ID is defined,
the code calls an action, which allows the widget to react to changes
made in the currency list.
Widgets share detailed information using context. For example,
the StoreContext is a store object method used to
invoke actions. The store object has an action method that uses the
action name and parameters to invoke actions on the store. Each action
should have a corresponding saga or reducer to update the store with
the new state.
Context provides a framework for component communication. The useContext React hook allows actions to read and access
default context to the widget. Use the useContext hook to obtain the context of an application. There are several
context that can be used within an application. The contexts used
by each widget indicates what methods and utilities will be available.
Context invokes actions and returns action methods.
useContext hook. The
storeContext allows you to get the value of a global variable, or a
reference to the applications state. Once you have referenced the action, use the action
function to dispatch actions. The action this example dispatches is the
_getCurrency action. This example passes in the payload, which is the
repository ID of the currency requested. This is described in the
/plugin/components/currency-selector/index.js file. Note that this is a portion
of the code example referenced
above:import React, {useContext} from 'react';
import {StoreContext} from '@oracle-retail-digital-commerce/react-ui/contexts';
...
// Make sure we have the latest currency list during client-side rendering.
const store = useContext(StoreContext);
useListCurrenciesFetcher(store);
// Invoked when the user selects a currency from the
// select element. Invokes the '_getCurrency' action to
// update the selected currency with info from the server.
const onCurrencyChange = event => {
const repositoryId = event.target.value;
if (repositoryId) {
store.action('_getCurrency', {repositoryId});
}
};getCurrency endpoint
is called with the given ID and a part of the state identified as myRepository.currencyInfo.selectedCurrency is updated. Because
the code is listening for changes in CurrencyInfo, it re-renders when there are changes. The following example uses
a <div> that is rendered when there is a selected
currency. This <div> contains a source string,
a displayName and a currencyCode. This is added to the /plugin/components/currency-selector/index.js file. Note that this is a portion of the code example referenced
above:
{selectedCurrency && (
<div className="CurrencySelector__SelectedCurrencyInfo">
{labelSelectedCurrency} {selectedCurrency.displayName}
({selectedCurrency.currencyCode})
</div>
)}When you access store data on your UI components, use selectors
and pass them using the connect method. It is important
that you use the correct selector method to fetch the precise information
required for rendering the UI component. There are a number of default
selectors that enable you to retrieve store state, however, if you
cannot find a selector that meets your needs, you will have to create
a custom selector. You can also use the useSelector hook to obtain part of the state information.
When you use the StoreContext hook to get the
store object, you must us an action method that uses
the action's name and parameters to invoke actions on the Redux store.
Each action must have a corresponding saga or reducer that updates
the store with the new state. Note that the action dispatch call should
happen only when using the useEffect hook. It is
best not to invoke any action calls in a React function component render method or useMemo hook.