I mimic code at https://github.com/primefaces/primereact/blob/master/src/components/utils/ClassNames.js
and
https://github.com/primefaces/primereact/blob/master/src/showcase/datatable/DataTablePaginatorDemo.js
My code
export function classNames(...args) {
if (args) {
let classes = [];
for (let i = 0; i < args.length; i++) {
let className = args[i];
if (!className) continue;
const type = typeof className;
if (type === 'string' || type === 'number') {
classes.push(className);
} else if (type === 'object') {
const _classes = Array.isArray(className)
? className
: Object.entries(className).map(([key, value]) =>
!!value ? key : null
);
classes = _classes.length
? classes.concat(_classes.filter((c) => !!c))
: classes;
}
}
return classes.join(' ');
}
return undefined;
}
import React, { useState, useEffect, useRef, Component } from 'react';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import { InputText } from 'primereact/inputtext';
import Moment from 'react-moment';
import { AcctgTransService } from '../service/AcctgTransService';
import { getTokenCookie } from '../TokenCookie';
import { Dialog } from 'primereact/dialog';
import { Dropdown } from 'primereact/dropdown';
import { Calendar } from 'primereact/calendar';
import { useTranslation } from 'react-i18next';
import { Const, ServiceHandle } from '../utilities';
import i18n from 'i18next';
import axios from 'axios';
import { Ripple } from 'primereact/core';
import { CustomerService } from '../service/CustomerService';
import { classNames } from 'utils/ClassNames.js';
export class AcctgTrans2 extends Component {
constructor(props) {
super(props);
this.state = {
customers1: [],
customers2: [],
customers3: [],
first1: 0,
rows1: 10,
first2: 0,
rows2: 10,
currentPage: 1,
pageInputTooltip: "Press 'Enter' key to go to this page.",
};
this.customerService = new CustomerService();
this.onCustomPage1 = this.onCustomPage1.bind(this);
this.onCustomPage2 = this.onCustomPage2.bind(this);
this.onPageInputKeyDown = this.onPageInputKeyDown.bind(this);
this.onPageInputChange = this.onPageInputChange.bind(this);
}
onCustomPage1(event) {
this.setState({
first1: event.first,
rows1: event.rows,
currentPage: event.page + 1,
});
}
onCustomPage2(event) {
this.setState({
first2: event.first,
rows2: event.rows,
});
}
onPageInputKeyDown(event, options) {
if (event.key === 'Enter') {
const page = parseInt(this.state.currentPage);
if (page < 0 || page > options.totalPages) {
this.setState({
pageInputTooltip: `Value must be between 1 and ${options.totalPages}.`,
});
} else {
const first = this.state.currentPage ? options.rows * (page - 1) : 0;
this.setState({
first1: first,
pageInputTooltip: "Press 'Enter' key to go to this page.",
});
}
}
}
onPageInputChange(event) {
this.setState({ currentPage: event.target.value });
}
componentDidMount() {
this.customerService
.getCustomersLarge()
.then((data) => this.setState({ customers1: data }));
this.customerService
.getCustomersLarge()
.then((data) => this.setState({ customers2: data }));
this.customerService
.getCustomersLarge()
.then((data) => this.setState({ customers3: data }));
}
render() {
const paginatorLeft = (
<Button type="button" icon="pi pi-refresh" className="p-button-text" />
);
const paginatorRight = (
<Button type="button" icon="pi pi-cloud" className="p-button-text" />
);
const template1 = {
layout:
'PrevPageLink PageLinks NextPageLink RowsPerPageDropdown CurrentPageReport',
PrevPageLink: (options) => {
return (
<button
type="button"
className={options.className}
onClick={options.onClick}
disabled={options.disabled}>
<span className="p-p-3">Previous</span>
<Ripple />
</button>
);
},
NextPageLink: (options) => {
return (
<button
type="button"
className={options.className}
onClick={options.onClick}
disabled={options.disabled}>
<span className="p-p-3">Next</span>
<Ripple />
</button>
);
},
PageLinks: (options) => {
if (
(options.view.startPage === options.page &&
options.view.startPage !== 0) ||
(options.view.endPage === options.page &&
options.page + 1 !== options.totalPages)
) {
const className = classNames(options.className, {
'p-disabled': true,
});
return (
<span className={className} style={{ userSelect: 'none' }}>
...
</span>
);
}
return (
<button
type="button"
className={options.className}
onClick={options.onClick}>
{options.page + 1}
<Ripple />
</button>
);
},
RowsPerPageDropdown: (options) => {
const dropdownOptions = [
{ label: 10, value: 10 },
{ label: 20, value: 20 },
{ label: 50, value: 50 },
{ label: 'All', value: options.totalRecords },
];
return (
<Dropdown
value={options.value}
options={dropdownOptions}
onChange={options.onChange}
appendTo={document.body}
/>
);
},
CurrentPageReport: (options) => {
return (
<span
className="p-mx-3"
style={{ color: 'var(--text-color)', userSelect: 'none' }}>
Go to{' '}
<InputText
size="2"
className="p-ml-1"
value={this.state.currentPage}
tooltip={this.state.pageInputTooltip}
onKeyDown={(e) => this.onPageInputKeyDown(e, options)}
onChange={this.onPageInputChange}
/>
</span>
);
},
};
const template2 = {
layout: 'RowsPerPageDropdown CurrentPageReport PrevPageLink NextPageLink',
RowsPerPageDropdown: (options) => {
const dropdownOptions = [
{ label: 10, value: 10 },
{ label: 20, value: 20 },
{ label: 50, value: 50 },
];
return (
<>
<span
className="p-mx-1"
style={{ color: 'var(--text-color)', userSelect: 'none' }}>
Items per page:{' '}
</span>
<Dropdown
value={options.value}
options={dropdownOptions}
onChange={options.onChange}
appendTo={document.body}
/>
</>
);
},
CurrentPageReport: (options) => {
return (
<span
style={{
color: 'var(--text-color)',
userSelect: 'none',
width: '120px',
textAlign: 'center',
}}>
{options.first} - {options.last} of {options.totalRecords}
</span>
);
},
};
return (
<div>
<div className="content-section implementation">
<div className="card">
<h5>Basic</h5>
<DataTable
value={this.state.customers1}
paginator
paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords}"
rows={10}
rowsPerPageOptions={[10, 20, 50]}
paginatorLeft={paginatorLeft}
paginatorRight={paginatorRight}>
<Column field="name" header="Name"></Column>
<Column field="country.name" header="Country"></Column>
<Column field="company" header="Company"></Column>
<Column
field="representative.name"
header="Representative"></Column>
</DataTable>
<h5>Custom Paginator Template</h5>
<DataTable
value={this.state.customers2}
paginator
paginatorTemplate={template1}
first={this.state.first1}
rows={this.state.rows1}
onPage={this.onCustomPage1}>
<Column field="name" header="Name"></Column>
<Column field="country.name" header="Country"></Column>
<Column field="company" header="Company"></Column>
<Column
field="representative.name"
header="Representative"></Column>
</DataTable>
<DataTable
value={this.state.customers3}
paginator
paginatorTemplate={template2}
first={this.state.first2}
rows={this.state.rows2}
onPage={this.onCustomPage2}
paginatorClassName="p-jc-end"
className="p-mt-6">
<Column field="name" header="Name"></Column>
<Column field="country.name" header="Country"></Column>
<Column field="company" header="Company"></Column>
<Column
field="representative.name"
header="Representative"></Column>
</DataTable>
</div>
</div>
</div>
);
}
}
Error
E:\monte\gsreact>npm run build
> gsreact#0.1.0 build
> react-scripts build && rmdir /s E:\xx\xx\runtime\base-component\webroot\screen\webroot\xx && move build E:\xx\xx\runtime\base-component\webroot\screen\webroot\xx
Creating an optimized production build...
Failed to compile.
.\src\components\AcctgTrans2.js
Cannot find module: 'utils/ClassNames.js'. Make sure this package is installed.
You can install this package by running: npm install utils/ClassNames.js.
E:\xx\xx>
How to fix this error?
Quick Solution
You can change:
import { classNames } from 'utils/ClassNames.js';
To:
import { classNames } from './utils/ClassNames.js';
Without the relative route, the import thinks it's a library.
Configuration
If you want to configure and you've used create-react-app: https://create-react-app.dev/docs/importing-a-component/#absolute-imports
In any other case:
https://github.com/tleunen/babel-plugin-module-resolver
https://github.com/entwicklerstube/babel-plugin-root-import
Related
I'm using react-table v7.8.0 to create a table with 4 columns. I'd like the the table to be sorted by columns date and views. My problem is that the table is only being sorted by the date column.
I have some pagination and manual sorting that appears to be working as expected. I think I've followed the documentation and I'm unsure where my mistake is.
The data is configured in the following file before being passed to a table component:
ViewsPageDayTable.js:
const postDateTemplate = { year: "numeric", month: "short", day: "numeric" }
import Link from "#/components/Link"
import { useMemo } from "react"
import siteMetadata from "#/data/siteMetadata"
import Table from "#/components/homeBrewAnalytics/table"
import LocationCountItem from "#/components/homeBrewAnalytics/locationCountItem"
export default function ViewsPageDayTable({ data }) {
const parsedData = []
for (var i = 0; i < data.length; i++) {
const dataRow = {}
for (var j in data[i]) {
if (j === "date") {
var date = new Date(data[i][j])
dataRow["date"] = date.toLocaleDateString(siteMetadata.locale, postDateTemplate)
} else if (j === "page") {
dataRow["page"] = data[i][j]
} else if (j === "country_count") {
var value = 0
if (data[i][j] == null) {
value = "unknown"
dataRow["country_count"] = null
} else {
value = data[i][j]
value = value.replaceAll("(", "[").replaceAll(")", "]").replaceAll("'", '"')
value = JSON.parse(value)
dataRow["country_count"] = value
}
} else if (j === "views") {
dataRow["views"] = parseInt(data[i][j])
}
}
parsedData.push(dataRow)
}
const PageCellProcessor = ({ value, row: { index }, column: { id } }) => {
return (
<Link href={value} className="hover:underline">
{" "}
{value}{" "}
</Link>
)
}
const LocationCellProcessor = ({ value }) => {
if (value == null) return <div></div>
const result = []
for (var z = 0; z < value.length; z++) {
result.push(<LocationCountItem value={value[z]} />)
}
return (
<div key={value} className="flex flex-shrink">
{result}{" "}
</div>
)
}
const columns = useMemo(
() => [
{
Header: "Date",
accessor: "date",
sortType: (a, b) => {
return new Date(b) - new Date(a)
},
},
{
Header: "Page",
accessor: "page",
Cell: PageCellProcessor,
},
{
Header: "Views",
accessor: "views",
sortType: 'basic',
},
{
Header: "Location",
accessor: "country_count",
Cell: LocationCellProcessor,
},
],
[]
)
return (
<div
id="viewsPerPagePerDay"
className="min-h-32 col-span-3 border-separate border-2 border-slate-800 p-3"
>
<Table columns={columns} data={parsedData} />
</div>
)
}
Table.js:
import { React, useMemo } from "react"
import { useTable, useFilters, useSortBy, usePagination } from "react-table"
import { useState } from "react"
export default function Table({ columns, data, isPaginated = true }) {
const [filterInput, setFilterInput] = useState("")
const handleFilterChange = (e) => {
const value = e.target.value || undefined
setFilter("page", value)
setFilterInput(value)
}
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
setFilter,
canPreviousPage,
canNextPage,
pageOptions,
nextPage,
previousPage,
state: { pageIndex, pageSize },
} = useTable(
{
columns,
data,
initialState: {
defaultCanSort: true,
disableSortBy: false,
manualSortBy: true,
sortBy: useMemo(
() => [
{
id: "date",
desc: false,
},
{
id: "views",
desc: true,
},
],
[]
),
pageIndex: 0,
pageSize: 15,
manualPagination: true,
},
},
useFilters,
useSortBy,
usePagination
)
return (
<>
<div className="flex justify-between align-middle">
<div> Page Views</div>
<input
className="mr-0 rounded-sm border border-gray-700 dark:border-gray-500 "
value={filterInput}
onChange={handleFilterChange}
placeholder={" Page name filter"}
/>
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
className={
column.isSorted ? (column.isSortedDesc ? "sort-desc" : "sort-asc") : ""
}
>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, _) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
})}
</tr>
)
})}
</tbody>
</table>
{Boolean(isPaginated) && (
<div id="pagination">
<div id="pageNum">
page {pageIndex + 1} of {pageOptions.length}
</div>{" "}
<div id="nextPrevButtons">
{canPreviousPage ? (
<div id="backButton" onClick={() => previousPage()}>
Previous
</div>
) : null}
{canNextPage ? (
<div id="nextButton" onClick={() => nextPage()}>
Next{" "}
</div>
) : null}
</div>
</div>
)}
</>
)
}
That was the problem with the error I got in the console.
The function is called on form submission. at first, it is working as expected but when calling this function on form submit. I get this following error.index.js:1 Warning: React has detected a change in the order of Hooks called by null. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks:
import React, { useCallback, useState } from 'react'
import { BigNumber } from '#ethersproject/bignumber'
import { TransactionResponse } from '#ethersproject/providers'
import { Currency, currencyEquals, ETHER, TokenAmount, WETH } from '#pancakeswap-libs/sdk'
import { Button, CardBody, AddIcon, Text as UIKitText } from '#pancakeswap-libs/uikit'
import { RouteComponentProps } from 'react-router-dom'
import { LightCard } from 'components/Card'
import { AutoColumn, ColumnCenter } from 'components/Column'
import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal'
import CardNav from 'components/CardNav'
import CurrencyInputPanel from 'components/CurrencyInputPanel'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import { AddRemoveTabs } from 'components/NavigationTabs'
import { MinimalPositionCard } from 'components/PositionCard'
import Row, { RowBetween, RowFlat } from 'components/Row'
import { PairState } from 'data/Reserves'
import { useActiveWeb3React } from 'hooks'
import { useCurrency } from 'hooks/Tokens'
import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
import { Field } from 'state/mint/actions'
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from 'state/mint/hooks'
import { useTransactionAdder } from 'state/transactions/hooks'
import { useIsExpertMode, useUserDeadline, useUserSlippageTolerance } from 'state/user/hooks'
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from 'utils'
import { maxAmountSpend } from 'utils/maxAmountSpend'
import { wrappedCurrency } from 'utils/wrappedCurrency'
import { currencyId } from 'utils/currencyId'
import Pane from 'components/Pane'
import ConnectWalletButton from 'components/ConnectWalletButton'
import useI18n from 'hooks/useI18n'
import AppBody from '../AppBody'
import { Dots, Wrapper } from '../Pool/styleds'
import { ConfirmAddModalBottom } from './ConfirmAddModalBottom'
import { PoolPriceBar } from './PoolPriceBar'
import { ROUTER_ADDRESS } from '../../constants'
export default function AddLiquidity({
match: {
params: { currencyIdA, currencyIdB },
},
history,
}: RouteComponentProps<{ currencyIdA?: string; currencyIdB?: string }>) {
const { account, chainId, library } = useActiveWeb3React()
const currencyA = useCurrency(currencyIdA)
const currencyB = useCurrency(currencyIdB)
const TranslateString = useI18n()
const oneCurrencyIsWBNB = Boolean(
chainId &&
((currencyA && currencyEquals(currencyA, WETH[chainId])) ||
(currencyB && currencyEquals(currencyB, WETH[chainId])))
)
const expertMode = useIsExpertMode()
// mint state
const { independentField, typedValue, otherTypedValue } = useMintState()
const {
dependentField,
currencies,
pair,
pairState,
currencyBalances,
parsedAmounts,
price,
noLiquidity,
liquidityMinted,
poolTokenPercentage,
error,
} = useDerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined)
const { onFieldAInput, onFieldBInput } = useMintActionHandlers(noLiquidity)
const isValid = !error
// modal and loading
const [showConfirm, setShowConfirm] = useState<boolean>(false)
const [attemptingTxn, setAttemptingTxn] = useState<boolean>(false) // clicked confirm
// txn values
const [deadline] = useUserDeadline() // custom from users settings
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
const [txHash, setTxHash] = useState<string>('')
// get formatted amounts
const formattedAmounts = {
[independentField]: typedValue,
[dependentField]: noLiquidity ? otherTypedValue : parsedAmounts[dependentField]?.toSignificant(6) ?? '',
}
// get the max amounts user can add
const maxAmounts: { [field in Field]?: TokenAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce(
(accumulator, field) => {
return {
...accumulator,
[field]: maxAmountSpend(currencyBalances[field]),
}
},
{}
)
const atMaxAmounts: { [field in Field]?: TokenAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce(
(accumulator, field) => {
return {
...accumulator,
[field]: maxAmounts[field]?.equalTo(parsedAmounts[field] ?? '0'),
}
},
{}
)
// check whether the user has approved the router on the tokens
const [approvalA, approveACallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_A], ROUTER_ADDRESS)
const [approvalB, approveBCallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_B], ROUTER_ADDRESS)
const addTransaction = useTransactionAdder()
async function onAdd() {
if (!chainId || !library || !account) return
const router = getRouterContract(chainId, library, account)
const { [Field.CURRENCY_A]: parsedAmountA, [Field.CURRENCY_B]: parsedAmountB } = parsedAmounts
if (!parsedAmountA || !parsedAmountB || !currencyA || !currencyB) {
return
}
const amountsMin = {
[Field.CURRENCY_A]: calculateSlippageAmount(parsedAmountA, noLiquidity ? 0 : allowedSlippage)[0],
[Field.CURRENCY_B]: calculateSlippageAmount(parsedAmountB, noLiquidity ? 0 : allowedSlippage)[0],
}
const deadlineFromNow = Math.ceil(Date.now() / 1000) + deadline
let estimate
let method: (...args: any) => Promise<TransactionResponse>
let args: Array<string | string[] | number>
let value: BigNumber | null
if (currencyA === ETHER || currencyB === ETHER) {
const tokenBIsBNB = currencyB === ETHER
estimate = router.estimateGas.addLiquidityETH
method = router.addLiquidityETH
args = [
wrappedCurrency(tokenBIsBNB ? currencyA : currencyB, chainId)?.address ?? '', // token
(tokenBIsBNB ? parsedAmountA : parsedAmountB).raw.toString(), // token desired
amountsMin[tokenBIsBNB ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), // token min
amountsMin[tokenBIsBNB ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), // eth min
account,
deadlineFromNow,
]
value = BigNumber.from((tokenBIsBNB ? parsedAmountB : parsedAmountA).raw.toString())
} else {
estimate = router.estimateGas.addLiquidity
method = router.addLiquidity
args = [
wrappedCurrency(currencyA, chainId)?.address ?? '',
wrappedCurrency(currencyB, chainId)?.address ?? '',
parsedAmountA.raw.toString(),
parsedAmountB.raw.toString(),
amountsMin[Field.CURRENCY_A].toString(),
amountsMin[Field.CURRENCY_B].toString(),
account,
deadlineFromNow,
]
value = null
}
setAttemptingTxn(true)
// const aa = await estimate(...args, value ? { value } : {})
await estimate(...args, value ? { value } : {})
.then((estimatedGasLimit) =>
method(...args, {
...(value ? { value } : {}),
gasLimit: calculateGasMargin(estimatedGasLimit),
}).then((response) => {
setAttemptingTxn(false)
addTransaction(response, {
summary: `Add ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(3)} ${currencies[Field.CURRENCY_A]?.symbol
} and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(3)} ${currencies[Field.CURRENCY_B]?.symbol}`,
})
setTxHash(response.hash)
})
)
.catch((e) => {
setAttemptingTxn(false)
// we only care if the error is something _other_ than the user rejected the tx
if (e?.code !== 4001) {
console.error(e)
}
})
}
const modalHeader = () => {
return noLiquidity ? (
<AutoColumn gap="20px">
<LightCard mt="20px" borderRadius="20px">
<RowFlat>
<UIKitText fontSize="48px" mr="8px">
{`${currencies[Field.CURRENCY_A]?.symbol}/${currencies[Field.CURRENCY_B]?.symbol}`}
</UIKitText>
<DoubleCurrencyLogo
currency0={currencies[Field.CURRENCY_A]}
currency1={currencies[Field.CURRENCY_B]}
size={30}
/>
</RowFlat>
</LightCard>
</AutoColumn>
) : (
<AutoColumn gap="20px">
<RowFlat style={{ marginTop: '20px' }}>
<UIKitText fontSize="48px" mr="8px">
{liquidityMinted?.toSignificant(6)}
</UIKitText>
<DoubleCurrencyLogo
currency0={currencies[Field.CURRENCY_A]}
currency1={currencies[Field.CURRENCY_B]}
size={30}
/>
</RowFlat>
<Row>
<UIKitText fontSize="24px">
{`${currencies[Field.CURRENCY_A]?.symbol}/${currencies[Field.CURRENCY_B]?.symbol} Pool Tokens`}
</UIKitText>
</Row>
<UIKitText small textAlign="left" padding="8px 0 0 0 " style={{ fontStyle: 'italic' }}>
{`Output is estimated. If the price changes by more than ${allowedSlippage / 100
}% your transaction will revert.`}
</UIKitText>
</AutoColumn>
)
}
const modalBottom = () => {
return (
<ConfirmAddModalBottom
price={price}
currencies={currencies}
parsedAmounts={parsedAmounts}
noLiquidity={noLiquidity}
onAdd={onAdd}
poolTokenPercentage={poolTokenPercentage}
/>
)
}
const pendingText = `Supplying ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)} ${currencies[Field.CURRENCY_A]?.symbol
} and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)} ${currencies[Field.CURRENCY_B]?.symbol}`
const handleCurrencyASelect = useCallback(
(currA: Currency) => {
const newCurrencyIdA = currencyId(currA)
if (newCurrencyIdA === currencyIdB) {
history.push(`/add/${currencyIdB}/${currencyIdA}`)
} else {
history.push(`/add/${newCurrencyIdA}/${currencyIdB}`)
}
},
[currencyIdB, history, currencyIdA]
)
const handleCurrencyBSelect = useCallback(
(currB: Currency) => {
const newCurrencyIdB = currencyId(currB)
if (currencyIdA === newCurrencyIdB) {
if (currencyIdB) {
history.push(`/add/${currencyIdB}/${newCurrencyIdB}`)
} else {
history.push(`/add/${newCurrencyIdB}`)
}
} else {
history.push(`/add/${currencyIdA || 'BNB'}/${newCurrencyIdB}`)
}
},
[currencyIdA, history, currencyIdB]
)
const handleDismissConfirmation = useCallback(() => {
setShowConfirm(false)
// if there was a tx hash, we want to clear the input
if (txHash) {
onFieldAInput('')
}
setTxHash('')
}, [onFieldAInput, txHash])
return (
<>
<CardNav activeIndex={1} />
<AppBody>
<AddRemoveTabs adding />
<Wrapper>
<TransactionConfirmationModal
isOpen={showConfirm}
onDismiss={handleDismissConfirmation}
attemptingTxn={attemptingTxn}
hash={txHash}
content={() => (
<ConfirmationModalContent
title={
noLiquidity
? TranslateString(1154, 'You are creating a pool')
: TranslateString(1156, 'You will receive')
}
onDismiss={handleDismissConfirmation}
topContent={modalHeader}
bottomContent={modalBottom}
/>
)}
pendingText={pendingText}
/>
<CardBody>
<AutoColumn gap="20px">
{noLiquidity && (
<ColumnCenter>
<Pane>
<AutoColumn gap="12px">
<UIKitText>{TranslateString(1158, 'You are the first liquidity provider.')}</UIKitText>
<UIKitText>
{TranslateString(1160, 'The ratio of tokens you add will set the price of this pool.')}
</UIKitText>
<UIKitText>
{TranslateString(1162, 'Once you are happy with the rate click supply to review.')}
</UIKitText>
</AutoColumn>
</Pane>
</ColumnCenter>
)}
<CurrencyInputPanel
value={formattedAmounts[Field.CURRENCY_A]}
onUserInput={onFieldAInput}
onMax={() => {
onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? '')
}}
onCurrencySelect={handleCurrencyASelect}
showMaxButton={!atMaxAmounts[Field.CURRENCY_A]}
currency={currencies[Field.CURRENCY_A]}
id="add-liquidity-input-tokena"
showCommonBases={false}
/>
<ColumnCenter>
<AddIcon color="textSubtle" />
</ColumnCenter>
<CurrencyInputPanel
value={formattedAmounts[Field.CURRENCY_B]}
onUserInput={onFieldBInput}
onCurrencySelect={handleCurrencyBSelect}
onMax={() => {
onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '')
}}
showMaxButton={!atMaxAmounts[Field.CURRENCY_B]}
currency={currencies[Field.CURRENCY_B]}
id="add-liquidity-input-tokenb"
showCommonBases={false}
/>
{currencies[Field.CURRENCY_A] && currencies[Field.CURRENCY_B] && pairState !== PairState.INVALID && (
<div>
<UIKitText
style={{ textTransform: 'uppercase', fontWeight: 600 }}
color="textSubtle"
fontSize="12px"
mb="2px"
>
{noLiquidity
? TranslateString(1164, 'Initial prices and pool share')
: TranslateString(1166, 'Prices and pool share')}
</UIKitText>
<Pane>
<PoolPriceBar
currencies={currencies}
poolTokenPercentage={poolTokenPercentage}
noLiquidity={noLiquidity}
price={price}
/>
</Pane>
</div>
)}
{!account ? (
<ConnectWalletButton width="100%" />
) : (
<AutoColumn gap="md">
{(approvalA === ApprovalState.NOT_APPROVED ||
approvalA === ApprovalState.PENDING ||
approvalB === ApprovalState.NOT_APPROVED ||
approvalB === ApprovalState.PENDING) &&
isValid && (
<RowBetween>
{approvalA !== ApprovalState.APPROVED && (
<Button
onClick={approveACallback}
disabled={approvalA === ApprovalState.PENDING}
style={{ width: approvalB !== ApprovalState.APPROVED ? '48%' : '100%' }}
>
{approvalA === ApprovalState.PENDING ? (
<Dots>Approving {currencies[Field.CURRENCY_A]?.symbol}</Dots>
) : (
`Approve ${currencies[Field.CURRENCY_A]?.symbol}`
)}
</Button>
)}
{approvalB !== ApprovalState.APPROVED && (
<Button
onClick={approveBCallback}
disabled={approvalB === ApprovalState.PENDING}
style={{ width: approvalA !== ApprovalState.APPROVED ? '48%' : '100%' }}
>
{approvalB === ApprovalState.PENDING ? (
<Dots>Approving {currencies[Field.CURRENCY_B]?.symbol}</Dots>
) : (
`Approve ${currencies[Field.CURRENCY_B]?.symbol}`
)}
</Button>
)}
</RowBetween>
)}
<Button
onClick={() => {
if (expertMode) {
onAdd()
} else {
setShowConfirm(true)
}
}}
disabled={!isValid || approvalA !== ApprovalState.APPROVED || approvalB !== ApprovalState.APPROVED}
variant={
!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]
? 'danger'
: 'primary'
}
width="100%"
>
{error ?? 'Supply'}
</Button>
</AutoColumn>
)}
</AutoColumn>
</CardBody>
</Wrapper>
</AppBody>
{pair && !noLiquidity && pairState !== PairState.INVALID ? (
<AutoColumn style={{ minWidth: '20rem', marginTop: '1rem' }}>
<MinimalPositionCard showUnwrapped={oneCurrencyIsWBNB} pair={pair} />
</AutoColumn>
) : null}
</>
)
}
I am not sure on how this should be done. Any ideas on what I could do?
Thanks
Im using Antd library and i can't seem to find where i have the bug.
This is my EditableTableCell component
import React, {Component} from 'react';
import { Form } from '#ant-design/compatible';
import '#ant-design/compatible/assets/index.css';
import { Input, InputNumber, Select, DatePicker } from "antd";
import moment from "moment";
import {EditableContext} from "./EditableTableRow";
const FormItem = Form.Item;
const Option = Select.Option;
class EditableTableCell extends Component {
getInput = (record, dataIndex, title, getFieldDecorator) => {
switch (this.props.inputType) {
case "number":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `Please Input ${title}!`
}
],
initialValue: record[dataIndex]
})(
<InputNumber formatter={value => value} parser={value => value} />
)}
</FormItem>
);
case "date":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
initialValue: moment(record[dataIndex], this.dateFormat)
})(<DatePicker format={this.dateFormat} />)}
</FormItem>
);
case "select":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
initialValue: record[dataIndex]
})(
<Select style={{ width: 150 }}>
{[...Array(11).keys()]
.filter(x => x > 0)
.map(c => `Product ${c}`)
.map((p, index) => (
<Option value={p} key={index}>
{p}
</Option>
))}
</Select>
)}
</FormItem>
);
default:
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `Please Input ${title}!`
}
],
initialValue: record[dataIndex]
})(<Input />)}
</FormItem>
);
}
}
render() {
const { editing, dataIndex, title, inputType, record, index,...restProps} = this.props;
return (
<EditableContext.Consumer>
{form => {
const { getFieldDecorator } = form;
return (
<td {...restProps}>
{editing ?
this.getInput(record, dataIndex, title, getFieldDecorator)
: restProps.children}
</td>
);
}}
</EditableContext.Consumer>
);
}
}
export default EditableTableCell;
This is my EditableTableCell component
import React, {Component} from 'react';
import { Form} from '#ant-design/compatible';
export const EditableContext = React.createContext();
class EditableTableRow extends Component {
render() {
return (
<EditableContext.Provider value={this.props.form}>
<tr {...this.props} />
</EditableContext.Provider>
);
}
}
export default EditableTableRow=Form.create()(EditableTableRow);
This is my ProductsPage component im having bug in
import React, {Component} from 'react';
import {Button, Layout, notification, Popconfirm, Space, Table,Typography} from "antd";
import {Link} from "react-router-dom";
import {Content} from "antd/es/layout/layout";
import EditableTableRow, {EditableContext} from "../components/EditableTableRow";
import EditableTableCell from "../components/EditableTableCell";
import API from "../server-apis/api";
import {employeesDataColumns} from "../tableColumnsData/employeesDataColumns";
import {CheckCircleFilled, InfoCircleFilled} from "#ant-design/icons";
class ProductsPage extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
error: null,
isLoaded: false,
editingKey: "",
errorMessage: "",
}
}
columns = [
...employeesDataColumns,
{
title: "Actions",
dataIndex: "actions",
width: "10%",
render: (text, record) => {
const editable = this.isEditing(record);
return editable ? (
<span>
<EditableContext.Consumer>
{form => (<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>Save</a>)}
</EditableContext.Consumer>
<a onClick={this.cancel}>Cancel</a>
</span>
) : (
<Space size="middle">
<a onClick={() => this.edit(record.username)}>Edit</a>
<Popconfirm title="Are you sure you want to delete this product?"
onConfirm={() => this.remove(record.username)}>
<a style={{color:"red"}}>Delete</a>
</Popconfirm>
</Space>
);
},
}
];
isEditing = (record) => {
return record.username === this.state.editingKey;
};
edit(username) {
this.setState({editingKey:username});
}
cancel = () => {
this.setState({ editingKey: ""});
};
componentDidMount() {
this.setState({ loading: true });
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
API.get(`users/all`,{ headers: { Authorization: token}})
.then(res => {
// console.log(res.data._embedded.productList);
const employees = res.data._embedded.employeeInfoDtoList;
this.setState({loading: false,data:employees });
})
}
async remove(username) {
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
API.delete(`/users/${username}`,{ headers: { Authorization: token}})
.then(() => {
let updatedProducts = [...this.state.data].filter(i => i.username !== username);
this.setState({data: updatedProducts});
this.successfullyAdded("Employee is deleted. It wont have any access to the website anymore.")
}).catch(()=>this.errorHappend("Failed to delete"));
}
hasWhiteSpace(s) {
return /\s/g.test(s);
}
saveData(form,username) {
form.validateFields((error, row) => {
if (error) {
return;
}
const newData = [...this.state.data];
const index = newData.findIndex(item => username === item.username);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row
});
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
const response = API.put(`/users/${username}/update`, row,{ headers: { Authorization: token}})
.then((response) => {
this.setState({ data: newData, editingKey: ""});
this.successfullyAdded("Empolyee info is updated")
})
.catch(error => {
this.setState({ errorMessage: error.message });
this.errorHappend("Failed to save changes.")
console.error('There was an error!', error);
});
});
}
successfullyAdded = (message) => {
notification.info({
message: `Notification`,
description:message,
placement:"bottomRight",
icon: <CheckCircleFilled style={{ color: '#0AC035' }} />
});
};
errorHappend = (error) => {
notification.info({
message: `Notification`,
description:
`There was an error! ${error}`,
placement:"bottomRight",
icon: <InfoCircleFilled style={{ color: '#f53333' }} />
});
};
render() {
const components = {
body: {
row: EditableTableRow,
cell: EditableTableCell
}
};
const columns = this.columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => {
const checkInput = index => {
switch (index) {
case "price":
return "number";
default:
return "text";
}
};
return {
record,
// inputType: col.dataIndex === "age" ? "number" : "text",
inputType: checkInput(col.dataIndex),
dataIndex: col.dataIndex,
title: col.title,
editing: this.isEditing(record)
};
}
};
});
const { data, loading } = this.state;
return (
<Layout>
<div>
<Link to="/add-product">
<Button style={{float:"right", background: "#0AC035",marginBottom:"1em", marginTop:"1em" }}
type="primary">New emplyee</Button>
</Link>
</div>
<Content>
<Table components={components} bordered dataSource={data} columns={columns} loading={loading} rowKey={data.username} rowClassName="editable-row"/>
</Content>
</Layout>
);
}
}
export default ProductsPage;
This is the bug I'm having:
enter image description here
And i want to have this result like its shown in Antd docs:
enter image description here
Id really appreciate if you take a look and help me figure out where im wrong
Updated Solution:
I find the issue. In render where you map the columns, you just return the column if it's not an editable column. You can check the code below. I added a check if it's dataIndex === 'actions', then return the following code:
Please Follow the link:
https://react-ts-v3fbst.stackblitz.io
Changes:
1.In columns, i remove the render function from the action object:
{
title: 'Actions',
dataIndex: 'actions',
width: '10%',
},
2. In render function where you map the columns, add the following code before this condition if(!col.editable) {,:
if (col.dataIndex === 'actions') {
return {
...col,
render: (text, record) => {
const editable = this.isEditing(record);
return editable ? (
<span>
<EditableContext.Consumer>
{(form) => (
<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>
Save
</a>
)}
</EditableContext.Consumer>
<a onClick={this.cancel}>Cancel</a>
</span>
) : (
<Space size='middle'>
<a onClick={() => this.edit(record.username)}>Edit</a>
<Popconfirm title='Are you sure you want to delete this product?' onConfirm={() => this.remove(record.username)}>
<a style={{ color: 'red' }}>Delete</a>
</Popconfirm>
</Space>
);
}
};
}
When you click on edit, you set the username as key for that particular row for editing, make sure you have username in each record. I tested this using the following data:
const data = [
{ id: 8, name: 'baun', model: '2022', color: 'black', price: 358, quantity: 3, username: 'brvim' },
{ id: 3, name: 'galileo', model: '20221', color: 'white', price: 427, quantity: 7, username: 'john' }
];
Most important, you should select that attribute as key that is unique in all records. As you are using username, i don't know what is your business logic or data looks like, but technically each record can have same username. So you must select something that would always be unique in your complete data.
I need an input box in the control box that search and filter the results, like this:
Input and search
Please help, I don't know how to implement this with react-select
Here is an example of implementation:
import React from "react";
import { render } from "react-dom";
import { makeData, Logo, Tips } from "./Utils";
import matchSorter from "match-sorter";
import Select from "react-select";
import "react-select/dist/react-select.css";
// Import React Table
import ReactTable from "react-table";
import "react-table/react-table.css";
class App extends React.Component {
constructor() {
super();
this.state = {
data: makeData(),
filtered: [],
select2: undefined
};
}
onFilteredChangeCustom = (value, accessor) => {
let filtered = this.state.filtered;
let insertNewFilter = 1;
if (filtered.length) {
filtered.forEach((filter, i) => {
if (filter["id"] === accessor) {
if (value === "" || !value.length) filtered.splice(i, 1);
else filter["value"] = value;
insertNewFilter = 0;
}
});
}
if (insertNewFilter) {
filtered.push({ id: accessor, value: value });
}
this.setState({ filtered: filtered });
};
render() {
const { data } = this.state;
return (
<div>
<pre>{JSON.stringify(this.state.filtered, null, 2)}</pre>
<br />
<br />
Extern Select2 :{" "}
<Select
style={{ width: "50%", marginBottom: "20px" }}
onChange={entry => {
this.setState({ select2: entry });
this.onFilteredChangeCustom(
entry.map(o => {
return o.value;
}),
"firstName"
);
}}
value={this.state.select2}
multi={true}
options={this.state.data.map((o, i) => {
return { id: i, value: o.firstName, label: o.firstName };
})}
/>
<ReactTable
data={data}
filterable
filtered={this.state.filtered}
onFilteredChange={(filtered, column, value) => {
this.onFilteredChangeCustom(value, column.id || column.accessor);
}}
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id;
if (typeof filter.value === "object") {
return row[id] !== undefined
? filter.value.indexOf(row[id]) > -1
: true;
} else {
return row[id] !== undefined
? String(row[id]).indexOf(filter.value) > -1
: true;
}
}}
columns={[
{
Header: "Name",
columns: [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Last Name",
id: "lastName",
accessor: d => d.lastName
}
]
},
{
Header: "Info",
columns: [
{
Header: "Age",
accessor: "age"
},
{
Header: "Over 21",
accessor: "age",
id: "over",
Cell: ({ value }) => (value >= 21 ? "Yes" : "No"),
filterMethod: (filter, row) => {
if (filter.value.indexOf("all") > -1) {
return true;
}
if (filter.value.indexOf("true") > -1) {
return row[filter.id] >= 21;
}
return row[filter.id] < 21;
},
Filter: ({ filter, onChange }) => {
return (
<select
onChange={event => {
let selectedOptions = [].slice
.call(event.target.selectedOptions)
.map(o => {
return o.value;
});
this.onFilteredChangeCustom(selectedOptions, "over");
}}
style={{ width: "100%" }}
value={filter ? filter.value : ["all"]}
multiple={true}
>
<option value="all">Show All</option>
<option value="true">Can Drink</option>
<option value="false">Can't Drink</option>
</select>
);
}
}
]
}
]}
defaultPageSize={10}
className="-striped -highlight"
/>
<br />
<Tips />
<Logo />
</div>
);
}
}
render(<App />, document.getElementById("root"));
Apollo client (2.6.3) with react.
Is it possible to fetch data in a method of a class component?
I'm building a global search component and I want to fetch data only when 3rd (and each subsequent) character is typed. Right now it is implemented with fetch api but I want to switch to apollo client and graphql api.
Until this moment I haven't gotten any problems with apollo client API cause I use
<Query/> component.
I've tried to use new hooks api but it turned out that useLazyQuery() can be used only in a function component (rules of hooks).
This is what I have done so far. I know that component is messy and I'm open to suggestions. This is my first react app.
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Select, Icon, Button } from "antd";
import { es } from "../../../utils/queries";
import { useLazyQuery } from "#apollo/react-hooks";
const { Option, OptGroup } = Select;
class GlobalSearch extends Component {
constructor(props) {
super(props);
this.searchInput = React.createRef();
}
state = {
data: [],
value: undefined,
display: false
};
componentDidMount() {
document.addEventListener("click", this.onClickOutside);
}
generateOptions(data) {
return data.map(d => {
if (d._index === "cases") {
d.label = (
<div>
<Icon style={{ fontSize: "18px" }} type="tool" />
<span style={{ color: "#183247" }}>
{d._source.number + d._source.serialNumber}
</span>
</div>
);
} else if (d._index === "items") {
d.label = (
<div>
<Icon style={{ fontSize: "18px" }} type="barcode-o" />
<span style={{ color: "#183247" }}>{d._source.name}</span>
</div>
);
} else if (d._index === "people") {
d.label = (
<div>
<Icon
type="user"
style={{ fontSize: "18px", float: "left", marginTop: "3px" }}
/>
<div style={{ marginLeft: "26px" }}>
<span style={{ color: "#183247" }}>
{" "}
<div>{d._source.name + " " + d._source.surname}</div>
</span>
<div>{d._source.email}</div>
</div>
</div>
);
} else if (d._index === "counterparties") {
d.label = (
<div>
<Icon style={{ fontSize: "18px" }} type="shop" />
<span style={{ color: "#183247" }}>{d._source.name}</span>
</div>
);
} else {
d.label = "undefined";
}
return d;
});
}
//group data according to type of the search result (index e.g. cases, items, contacts)
setData = es_data => {
const data = {};
const map = new Map();
for (const item of es_data) {
if (!map.has(item._index)) {
map.set(item._index);
data[item._index] = [];
}
data[item._index].push(item);
}
this.setState({ data: data });
};
handleSearch = value => {
if (value.length > 2) {
//tutaj wyszukujemy dane na serwerze a wyniki callbackiem przesyĆamy do state.data[]
//response.json() calls mixin methods from body
const host = window.location.hostname
// const { loading, data } = useLazyQuery(es(value));
// if (loading) return undefined;
// if (data) {
// this.setData(this.generateOptions(data));
// }
fetch(`http://${host}:3000/api/es?searchString=${value}`)
.then(response => response.json())
.then(es_data => {
this.setData(this.generateOptions(es_data));
});
}
};
handleChange = value => {
//przy kazdym wpisanym znaku aktualizuj wartosc pola
this.setState({ value });
};
handleSelect = (key, option) => {
console.log(key);
console.log(option);
};
handleBlur = e => {
this.setState({ display: false, value: undefined, data: [] });
};
onClick = () => {
this.setState({ display: !this.state.display });
};
getSearchField(options) {
if (this.state.display) {
return (
<Select
id="global-search-field"
optionLabelProp="label"
showSearch
value={this.state.value}
placeholder={"search..."}
style={{ display: this.state.display, width: "350px" }}
defaultActiveFirstOption={true}
showArrow={false}
filterOption={false}
onSearch={this.handleSearch}
onChange={this.handleChange}
onSelect={this.handleSelect}
notFoundContent={"nothing here..."}
onBlur={this.handleBlur}
autoFocus={true}
showAction={["focus"]}
dropdownClassName="global-search-dropdown"
>
{options}
</Select>
);
}
}
render() {
//generate options for each group (index)
const options = Object.keys(this.state.data).map(d => (
<OptGroup label={d}>
{this.state.data[d].map(d => (
<Option
className={d._index}
type={d._index}
key={d._id}
label={d.label}
>
<div>{d.label}</div>
</Option>
))}
</OptGroup>
));
return (
<span>
{this.getSearchField(options)}
<Button
id="global-search"
shape="circle"
icon="search"
onClick={this.onClick}
/>
</span>
);
}
}
export default GlobalSearch;
Apollo Client offers a Promise-based API that provides more imperative control over when/where your queries/mutations are run. Here's an example from their docs:
client
.query({
query: gql`
query GetLaunch {
launch(id: 56) {
id
mission {
name
}
}
}
`,
variables: { ... }
})
.then(result => console.log(result));
You can import your client and use this pattern in any method or function like any other async operation using Promises.