У меня есть внешний компонент с методом рендеринга, подобным этому:
render()
: JSX.Element
{
return (
<React.Fragment>
<QueryGetCustomersPaginated
query={Q_GET_CUSTOMERS}
variables={{ limit: this.props.limit, offset: this.state.offset }}
pollInterval={1000}
onCompleted={()=>console.log('Data Loading Completed!')}
>
{({ loading, error, data, startPolling, stopPolling }) =>
{
if (loading)
{
return "Loading..."
}
if (error)
{
return `Error: ${error.message}`
}
if (data)
{
// Pass Data to Private Properties
this.customers = data.getCustomers.customers
this.totalRecords = data.getCustomers.metadata.totalRecords
// Build UI
return (
<React.Fragment>
{/* PAGE TITLE */}
<h2 className="text-center mb-3">Lista de Clientes</h2>
{/* Customer List */}
<ul className="list-group">
{
this.customers.map(customer => (
<CustomerItem customer={(customer as Customer)} key={customer.id} />
))
}
</ul>
{/* Pagination */}
<Paginator
maxRangeSize={3}
pageSize={this.props.limit}
totalRecords={this.totalRecords}
currentPage={this.state.currentPage}
onPageChange={(newOffset: number, newPage: number) => this.setPageFor(newOffset, newPage)}
/>
</React.Fragment>
)
}
}}
</QueryGetCustomersPaginated>
</React.Fragment>
)
}
У меня также есть обработчик событий для подкомпонента (Paginator) onPageChanged
, например:
private setPageFor(offset: number, page: number)
{
this.setState({
offset : offset,
currentPage : page
})
}
и, наконец, подкомпонент выглядит следующим образом:
import React, { SyntheticEvent } from 'react'
//---------------------------------------------------------------------------------
// Component Class
//---------------------------------------------------------------------------------
export class Paginator extends
React.PureComponent<IPaginatorProps, IPaginatorState>
{
//-------------------------------------------------------------------------
constructor(props: IPaginatorProps)
{
// Calls Super
super(props)
// Initialize State
this.state = {
initialPageInRange : 1,
currentPage : 1
}
}
//-------------------------------------------------------------------------
public render(): JSX.Element
{
return (
<div className="row justify-content-center mt-3">
<nav>
<ul className="pagination">
{this.renderPageItems()}
</ul>
</nav>
</div>
)
}
//-------------------------------------------------------------------------
private renderPageItems(): JSX.Element[]
{
// Return Value
const items: JSX.Element[] = []
for (let iCounter: number = 0; iCounter < this.getMaxPossibleRange(); iCounter++)
{
items.push(
// className won't be set until third time a link is clicked..
<li key={iCounter} className={(**this.state.currentPage** === (this.state.initialPageInRange + iCounter)) ?
'page-item active' : 'page-item'}>
<a className="page-link"
href="#"
onClick={(e: SyntheticEvent) => this.goToPage(this.state.initialPageInRange + iCounter, e)}
>
{this.state.initialPageInRange + iCounter}
</a>
</li>
)
}
return items
}
//-------------------------------------------------------------------------
private goToPage(page: number, e: SyntheticEvent)
{
e.preventDefault()
const newOffset: number = this.getOffset(page)
const newPage: number = this.getCurrentPage(newOffset)
this.setState({
currentPage: newPage
})
this.props.onPageChange(newOffset, newPage)
}
//-------------------------------------------------------------------------
private getOffset(currentPage: number): number
{
return ((currentPage - 1) * this.props.pageSize)
}
//-------------------------------------------------------------------------
private getCurrentPage(offset: number): number
{
return ((Math.ceil(offset / this.props.pageSize)) + 1)
}
//-------------------------------------------------------------------------
private getTotalPages(): number
{
return (Math.ceil(this.props.totalRecords / this.props.pageSize))
}
//-------------------------------------------------------------------------
private getMaxPossibleRange(): number
{
return (this.props.maxRangeSize <= this.getTotalPages()) ?
this.props.maxRangeSize : this.getTotalPages()
}
}
//---------------------------------------------------------------------------------
// Component Interfaces
//---------------------------------------------------------------------------------
interface IPaginatorProps
{
maxRangeSize : number // 3
pageSize : number // 3
totalRecords : number // 19
currentPage : number // 1
onPageChange : (newOffset: number, newPage: number) => void
}
//---------------------------------------------------------------------------------
interface IPaginatorState
{
initialPageInRange : number
currentPage : number
}
Как видите, у нас есть основной компонент, который выдает запрос Apollo с параметром смещения (установленным в состояние), который обновляется каждый раз, когда setPageFor (offset)вызывается по мере обновления состояния и повторного рендеринга компонента.
Теперь в подкомпоненте у меня есть два способа получения значения currentPage, один из которых - использование реквизитов, переданных из родительского компонента (в данном случае клиентов), а другой - использование состояния компонента для установкиначальное значение 1 и обновлять состояние при каждом нажатии на любой странице ссылки.
Ссылка на страницу при щелчке вызывает goToPage (), которая устанавливает локальное состояние и запускает событие изменения страницы на вызывающую сторону (компонент клиентов).
Текущее поведение этого компонента не заключается в изменении тега <li>
className на 'active' до тех пор, пока в третий раз не будет нажата какая-либо ссылка.
Я пытался использовать shouldComponentUpdate()
и расширять React.PureComponent, я даже пытался установить тайм-ауты (я никогда не писал это ...), чтобы проверить, работает ли это, но каким-то образом результат всегда одинаков.
Теперь, если я прокомментирую строку, которая запускает событие onPageChanged()
в методе goToPage()
, подкомпонент для разбивки на страницы отрисовывается отлично, но если я этого не сделаю, и субкомпоненту разрешено отправить событие вродительский объект перерисовывает все дерево, и это похоже на то, что подкомпонент был перемонтирован, что привело к удалению его состояния.
Странная часть в том, что в третий раз, когда я нажимаю на любую ссылку, все работает, как ожидалось, два раза выходит из строя и работает третий.
Я полностью озадачен этим моментом после нескольких часов моих выходных, анализируя этот код с помощью отладчика.
Я прошу прощения за копирование и вставку всего этого кода, но я считаю, что было необходимо получить представление о том, чего я пытаюсь достичь здесь.
PS На данный момент проблему можно решить, используя реквизиты для передачи свойства currentPage
от компонента customer, который является вызывающей стороной подкомпонента (для разбиения на страницы).
Я работаю с React уже несколько месяцев, и это первый раз, когда я получаю и странно ошибаюсь, поэтому мне бы очень хотелось узнать, почему происходит такое поведение и почему состояние теряется.Первые два раза компонент рендерится.Также с целью повторного использования компонента, я считаю, что лучше хранить эти две переменные в состоянии.Любые мысли от тех, кто знает React лучше, будут высоко оценены, или это проблема библиотеки Apollo?
Спасибо.