Search code examples
javascriptreact-nativenext.jsreact-hooksantd

Answered: children's onChange was not Updating the Parent : Ant Design `Select` and `Table` inside `Tab` was not changing according to `pageSize`


The question is: How can I print "page + new page: 10 + 10" in the console?

I have a part of code like below for (somewhat long Operate page)

const defaultPageSize = 5;//operateConstants.OP_DEFAULT_ITEM_PAGESIZE;
const MemberManagement: React.FC = () => {
  //table page size dropdown
  const [pageSize, setPageSize] = useState(defaultPageSize);
  useEffect(() => {
    console.log("pageSize changed to " + pageSize);
  }, [pageSize]);

  const derivedPageSize = pageSize * 2;
  console.log("derivedPageSize: " + derivedPageSize);

  const handlePageSizeChange = (newPageSize: number) => {
    setPageSize(newPageSize);
    console.log("page + new page: " + pageSize + " + " + newPageSize);
    message.info("page + new page: " + pageSize + " + " + newPageSize);
    let newPageSize2 = returnV();
    console.log("Using function returnV : " + newPageSize2);
    setPageSize(newPageSize2);
  };

  function returnV(){ return derivedPageSize;}
}
export default MemberManagement;

The message prints "page + new page: 5 + 10" when I need it to print "page + new page: 10 + 10".

So, I tried to console log using useEffect, and it printed "pageSize changed to 10" and "derivedPageSize: 20" when I changed the pageSize value to 10.

I thought this was easy; if I make a function call returnV returning derivedPageSize, it should print the changed value. But it printed "Using function returnV : 10" when it's supposed to be 20, values not updated like handlePageSizeChange [(console_Log_For_PageSize_Change)](https://i.sstatic.net/kQJ1p.png) . I don't know how to print "page+newpage:10 + 10" inside the handlePageSizeChange.

However, in my other page(CS page), setPageSize works fine like this, which is confusing.

const defaultPageSize = csConstants.CS_DEFAULT_PAGESIZE;
export default function Tcs() {
  //table page size dropdown
  const [pageSize, setPageSize] = useState(defaultPageSize);
  const handlePageSizeChange = (newPageSize: number) => {
    setPageSize(newPageSize);
  };
};

I have asked chat-gpt and Google, useCallback from React, tried setting the key=pageSize for the Table element to re-render when pageSize gets updated, tried making a function like returnV. I think When useState update then map loop is not working in array inside of object in useState. React js had a similar question, but with array type.

I think the problem has to do with the timing of rendering or compiling with the const setPageSize, along with the code size but I have no clue where the error is. So, if you know the reason I cannot call handlePageSizeChange and have setPageSize resetting my pageSize like my other page(CS), please explain for me.

(1)edited to show Reproducible Code____________________below

The another question is: I think it has to do with elements rendered within another element, but in my eyes rn, Operate and CS page's handlePageSizeChange looks pretty much the same, but one works while other one doesn't. So why is that?

Reproducible Code for (Needs fixing :: Operate page), (functioning :: CS page) below:

(Needs fixing :: Operate page)

'use client';

import React, { useState, useEffect } from 'react';
import { Row, Col, Select, message, Tabs, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { TableRowSelection } from 'antd/es/table/interface';
import * as styles from '@/constants/styles';

//range-picker

// Profile Information : Item
interface DataProfileType {
  ID: number | string;
  ITEM_NAME: string;
  DATE_OF_ACQUISITION: Date | string;
}

const ProfileInformationData: DataProfileType[] = [];
for (let i = 0; i < 20; i++) {
  ProfileInformationData.push({
    ID: i >= 9 ? 2023 + `${i + 1}` : 2023 + `0${i + 1}`,
    ITEM_NAME: 'name',
    DATE_OF_ACQUISITION:('YYYY-MM-DD-HH-mm-ss'),
  });
}

const ProfileInformation: ColumnsType<DataProfileType> = [
  { title: 'ID', dataIndex: 'ID', align: 'center' },
  { title: 'item', dataIndex: 'ITEM_NAME', align: 'center' },
  { title: 'date', dataIndex: 'DATE_OF_ACQUISITION', align: 'center' },

];

//item data-table pageSize dropdown
const ALIST = {
  "0": "pageSizeSelection",
  "5": "count by 5",
  "10": "count by 10",
  "20": "count by 20"
}
const dropdownList: { [key: string]: string } = ALIST;
const itemPageProps = Object.keys(ALIST).map((key) => ({
  value: Number(key),
  label: dropdownList[key],
  disabled: Number(key) === 0,
}));
const defaultPageSize = 5;

const Operate: React.FC = () => {
  const [pageSize = defaultPageSize, setPageSize] = useState<number>();

  useEffect(() => {
    console.log("pageSize " + pageSize);
  }, [pageSize]);

  const handlePageSizeChange = (newPageSize: number) => {
    setPageSize(newPageSize);
    console.log("page + new page: " + pageSize + " + " + newPageSize);
    message.info("page + new page: " + pageSize + " + " + newPageSize);
  };

  const rowSelection: TableRowSelection<DataProfileType> = {
    onChange: (selectedRowKeys, selectedRows) => {
      console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
    },
  };
  // tab 1
  const InitialSetting = () => {
    return (
      <>
      <Row>
          <Select //
            key={pageSize}
            size="middle"
            value={pageSize}
            options={itemPageProps}
            defaultValue={defaultPageSize}
            onChange={handlePageSizeChange}
            className="w-[150px]"
          />
          </Row>
        <Row>
          <Col span={24}>
            <Table
              rowKey={(record) => record.ID}
              columns={ProfileInformation}
              dataSource={ProfileInformationData}
              rowSelection={rowSelection}
              bordered
              size="middle"
              pagination={{
                showSizeChanger: false,
                pageSize: pageSize,
                position: ['bottomCenter'],
              }}
              className={styles.darkline_t}
            />

          </Col>
        </Row>
      </>
    );
  };

  //tab 2
  const NFTInformation = () => {
    return <>tab 2</>;
  };

  const initialItems = [
    { label: 'defaultTab', key: '1', children: InitialSetting() },
    { label: 'Tab2', key: '2', children: NFTInformation() },
  ];

  const [items, setItems] = useState(initialItems);

  return (
    <>
          <Row gutter={12}>
        <Col span={24}>
          <Tabs defaultActiveKey="1" type="card" size={'middle'} items={items} />
        </Col>
      </Row>
    </>
  );

};

export default Operate;

(functioning :: CS page)

'use client';

import React, { useState } from 'react';
import { Space, Select } from 'antd';
import { Col, Row, Table, message } from 'antd';
import type { TableProps} from 'antd';
import type { ColumnsType, SorterResult } from 'antd/es/table/interface';

//data-table orderBy dropdown
const CS_1 = {
  "0": "--",
  "col_key1d": "sort1",
  "col_key5d": "sort1",
  "col_key5a": "sort1",
  "col_key6d": "sort1",
  "col_key6a": "sort1",
};
const CS_2 = {
  "0": "pageSize",
  "5": "5pageSize",
  "10": "10pageSize",
  "20": "20pageSize"
};
const CS_DATA_DROPDOWN1: { [key: string]: string } = CS_1;
const menuProps1 = Object.keys(CS_DATA_DROPDOWN1).map((key) => ({
  value: key,
  label: CS_DATA_DROPDOWN1[key],
  disabled: key === "0",
}));

const CS_DATA_DROPDOWN2: { [key: string]: string } = CS_2;
//data-table pageSize dropdown
const menuProps2 = Object.keys(CS_DATA_DROPDOWN2).map((key) => ({
  value: Number(key),
  label: CS_DATA_DROPDOWN2[key],
  disabled: Number(key) === 0,
}));

//----------------------------------------------------------Default Values
const defaultPageSize = 5;
const defaultOrderBy = "col_key1d";
//----------------------------------------------------------end Default Values
//main
export default function CS() {

  //table order dropdown
  const [orderBy, setOrderBy] = useState(defaultOrderBy);
  const handleOrderByChange = (newOrderBy: string) => {
    setOrderBy(newOrderBy);
    const order = newOrderBy.endsWith("d") ? "descend" : "ascend";
    const col = newOrderBy.substring(0, newOrderBy.length - 1);
    const columnKey = col as keyof DataType;
    setSortedInfo({ columnKey, order });
  };

  //table page size dropdown
  const [pageSize, setPageSize] = useState(defaultPageSize);
  const handlePageSizeChange = (newPageSize: number) => {
    console.log("page + new page: " + pageSize + " + " + newPageSize);
    message.info("page + new page: " + pageSize + " + " + newPageSize);
    setPageSize(newPageSize);
  };
  //----------------------------------------------------------end dropdown

  //----------------------------------------------------------data-table
  const [sortedInfo, setSortedInfo] = useState<SorterResult<DataType>>({
    columnKey: defaultOrderBy.substring(0, defaultOrderBy.length - 1),
    order: defaultOrderBy.endsWith("d") ? "descend" : "ascend"
  });

  const [loading, setLoading] = useState(false);

  interface DataType {
    key: React.Key;
    col_key1: string,
     col_key5: string; col_key6:string;
  }
  const data: DataType[] = [];
  
  for (let i = 0; i < 100; i++) {
    data.push({
      key: i,
      col_key1: `${i} a-`,
      col_key5: `2023-03-14-01-01-01`,
      col_key6: `2023-03-15-01-01-02`,
    });
  }

  const handleChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter) => {
    console.log('Various parameters', pagination, filters, sorter);
    setSortedInfo(sorter as SorterResult<DataType>);
  };
  const columns: ColumnsType<DataType> = [
    {
      title: 'a1',
      dataIndex: 'col_key1',
      key: 'col_key1',
      align: 'center',
      width: 120,
      sortOrder: sortedInfo.columnKey === 'col_key1' ? sortedInfo.order : null,
    },
    {
      title: 'd',
      dataIndex: 'col_key5',
      key: 'col_key5',
      align: 'center',
      
      sortOrder: sortedInfo.columnKey === 'col_key5' ? sortedInfo.order : null,
    },
    {
      title: <div className="text-center">e</div>,
      dataIndex: 'col_key6',
      key: 'col_key6',
      align: 'center',
      sorter: (a, b) => a.col_key6.length - b.col_key6.length,
      sortOrder: sortedInfo.columnKey === 'col_key6' ? sortedInfo.order : null,
    },
  ];

  //---------------------------------------------------------- end data-table

  //main
  return (
    <>
      <Row justify={'space-between'} align={'middle'} className="mt-4">
        <Col xs={24} md={12} className="flex flex-wrap justify-end">
          <Space size={'middle'} className="flex flex-wrap">
            <Select
              value={orderBy}
              defaultValue={defaultOrderBy}
              size="middle"
              options={menuProps1}
              onChange={handleOrderByChange}
              className="w-[150px]"
            />
            <Select
              size="middle"
              value={pageSize}
              options={menuProps2}
              defaultValue={defaultPageSize}
              onChange={handlePageSizeChange}
              className="w-[150px]"
            />
          </Space>
        </Col>
      </Row>
      <Row>
        <Col span={24}>
          <Table columns={columns} dataSource={data} loading={loading} onChange={handleChange}
            pagination={{
              position: ["bottomCenter"], pageSize: pageSize, onShowSizeChange: handlePageSizeChange,
              showSizeChanger: false, // set to false to hide pageSizeOptions
            }} scroll={{ x: 'max-content' }}
          />
        </Col>
      </Row>

    </>
  );
};

(2)edited to show Reproducible Code Online______________below

https://codesandbox.io/p/sandbox/wonderful-wave-2e6bof?file=%2Fpages%2Foperate.tsx&selection=%5B%7B%22endColumn%22%3A17%2C%22endLineNumber%22%3A97%2C%22startColumn%22%3A17%2C%22startLineNumber%22%3A97%7D%5D

(3)edited title______________Answered Question

JS constant question (using React,NextJS, Ant Design) :: const [pageSize, setPageSize] = useState(defaultPageSize); setPageSize is not working

to

children's onChange was not Updating the Parent Element :: Ant Design Select and Table inside Tab was not changing according to changed pageSize

setPageSize was working fine, I had problems with children and parenting the code.

Answer: in using Tabs, I needed to use Tab with {initialItems}, bringing the children(initialItems) elements to the same level with the parents(Tab).

          <Tabs defaultActiveKey="1">
            {initialItems.map(item => (
              <Tabs.TabPane tab={item.label} key={item.key}>
                {item.children}
              </Tabs.TabPane>
            ))}
          </Tabs>

instead of children(initialItems) inside children(items) inside parent(Tab)

const initialItems = [
    { label: 'Tab1', key: '1', children: InitialSetting() },
    { label: 'Tab2', key: '2', children: NFTInformation() },
  ];
  const [items, setItems] = useState(initialItems);
<Tabs defaultActiveKey="1" type="card" size={'middle'} items={items} />

as changes in children(initialItems) were not detected in parent(Tab) element.

Thank you for helping out!


Solution

  • setPageSize enqueues changes to the component state. It tells React that this component and its children need to re-render with the new state. And re-render will begin some later time, after the execution of handlePageSizeChange function. Problem is you are reading the old value console.log("page + new page: " + pageSize + " + " + newPageSize); of the pageSize state in this line. Because the component is not re-rendered yet with the updated value.

    I am not sure whats the use case here for you to get the same result for both pageSize, and newPageSize, whereas you already have the updated value in newPageSize argument.