imballinst / react-bs-datatable

Bootstrap datatable without jQuery. Features include: filter, sort, pagination, checkbox, and control customization.
https://imballinst.github.io/react-bs-datatable
MIT License
60 stars 20 forks source link

Semi-Controlled Sort Table doesn't work on production build #185

Closed devMagno closed 1 year ago

devMagno commented 1 year ago

Hello again! I'm the guy who implemented the partial controlled table, by using the useDatatableWrapper, you helped me in this issue and it worked just fine, but in my prod env or even when building the project locally by using vite build && vite preview it doesn't seem to work and I don't know why, no errors are logged to the console

The 'Todos' column is set to be the default sort, but it seems to be sorting by the 'Unidade' column and interacting with it by clicking other sortable columns also doesn't work image

If it helps, my code looks something like this:

function Page() {
    const initialSort = { prop: 'abertos_total_sum', order: 'asc' }

    const [ordenacao, setOrdenacao] = useState(initialSort)

    const handleChangeSort = (sort) => {
        if (!sort)
            return

        if (ordenacao.prop === sort.prop) {
            setOrdenacao((prev) => ({ prop: sort.prop, order: prev.order === 'asc' ? 'desc' : 'asc' }))
        } else {
            setOrdenacao(sort)
        }
    }

    function SemiControlledTableSetter({ sortState }) {
        const { onSortChange } = useDatatableWrapper()

        useEffect(() => {
            onSortChange(sortState)
        }, [onSortChange, sortState])

        return null
    }

    return (
        <>
            <DatatableWrapper 
                body={dadosTabela}
                headers={headers}
                paginationOptionsProps={{
                    initialState: {
                            rowsPerPage: 10,
                            options: [],
                   },
               }}
            >
                <SemiControlledTableSetter sortState={ordenacao} />

                <table>
                   {/* my custom tableHead component, idk if the code from inside the component matters, lmk if it does */}
                   <TableHead
                       ordenacaoAtual={ordenacao}
                       handleChangeSort={handleChangeSort}
                       unidadeSelecionada={unidadeSelecionada}
                   />

                  {/* tablebody from react-bs-datatable */}
                  <TableBody>
                       {(rows) => (
                           // .....
                       )}
                  </TableBody>
                </table>
           </ DatatableWrapper>
        </>
    )
}
imballinst commented 1 year ago

Thanks @devMagno for the thorough report! Looking at it now...

imballinst commented 1 year ago

That's very curious that it works OK in dev but it doesn't work in prod. I don't think I remember adding that behavior somewhere. I tried to reproduce it in the examples folder in this repository, with the following snippet:

https://github.com/imballinst/react-bs-datatable/blob/125ac00b39dbde21c603be9fcecccc47750d6e98/examples/vite-semi-controlled/src/App.tsx#L78-L89

so I tried to "semi-control" the filter and sort state from outside, and it seemed to work. Could you try the following?

  1. Clone this repository
  2. Go to examples/vite-semi-controlled folder
  3. Do yarn, then yarn build, then yarn preview
  4. Type anything in input and/or change the sort order and see if it changes the table state

If I'm about to debug your code, probably what I am going to do first is to log the value of ordenacao. Probably 2 things that we can look for here:

  1. Does it trigger re-render?
  2. If it does, does the value of ordenacao change?
devMagno commented 1 year ago

Ok. I'll check the example out, but yes, ordenacao triggers re-render and the value changes and it shows in the UI: image

So, what I mean is: the state is changing and it is triggering re-renders, but somehow it doesn't change the table sorting, but I'll take a look at the example and see how I can improve my code

imballinst commented 1 year ago

So, what I mean is: the state is changing and it is triggering re-renders, but somehow it doesn't change the table sorting, but I'll take a look at the example and see how I can improve my code

I see, hmm. Does the same happen for filters, or is it only for sort?

devMagno commented 1 year ago

Actually, idk because I'm not using custom filters

imballinst commented 1 year ago

Oh sorry, by filter I meant filter text, if you write something on the filter input, does the table data change? (I want to make sure if this is only sort issue or also filter issue)

devMagno commented 1 year ago

Yes, it works properly image

devMagno commented 1 year ago

I've tried some changes and it still doesn't work:

After I've moved the component code out of the page fn, it stopped working on dev server as well

imballinst commented 1 year ago

Interesting, ok, could you try this?

function SemiControlledTableSetter({ sortState }) {
    const { onSortChange, sortState: internalSortState  } = useDatatableWrapper()
    console.log("internalSortState", internalSortState, "sortState", sortState)
    useEffect(() => {
        onSortChange(sortState)
    }, [onSortChange, sortState])

    return null
}

So what I want to check out is if the internal sort state actually changes or not.

devMagno commented 1 year ago

Hmm, it doesn't seem to be changing

My component code:

function TableController(props) {
    const { ordenacao } = props

    const { onSortChange, sortState: internalSortState  } = useDatatableWrapper()

    console.log("internalSortState", internalSortState)
    console.log("ordenacao", ordenacao)

    useEffect(() => {
        onSortChange(ordenacao)
    }, [onSortChange, ordenacao])

    return null
}

Log output: image

Somehow the sortState is always {order: 'asc', prop: 'producao_unidade'}, which is my first sortable header item

I'm using version ^3.11.2 btw

devMagno commented 1 year ago

if it helps, these are my headers: image

imballinst commented 1 year ago

Interesting, I have a bit of suspect on what might be causing the issue, I'll try to experiment on it. In the meantime, could you try wrapping the headers with a useMemo? Something like:

const headers = useMemo(() => [...], [unidadeSelectionada]);
devMagno commented 1 year ago

Wow, it works now! But.... only when state changes, not on first render

First render: image

After changing: image

On first render the internalSortState.prop is still producao_unidade

imballinst commented 1 year ago

Alright, nice, we've made progress! That's interesting, gimme a bit of time to debug it further, I think it should change it during the first render... perhaps I missed something. I'll get back to this when I have found a clue.

imballinst commented 1 year ago

@devMagno I think we might need to pass sortProps: { initialState } to DatatableWrapper, something like this:

<DatatableWrapper sortProps={{ initialState: ordenacao }}>

Unfortunately with how React useEffect order works, the useEffect in <TableController /> will be fired first, only for later to be overridden by the internal useEffect of DatatableWrapper... so we have to explicitly set the initial state in the DatatableWrapper props.

Let me know if it solves your issue, thanks!

devMagno commented 1 year ago

Oh, ok! I'll try it out

devMagno commented 1 year ago

It works exactly as I expected, thank you so much!

imballinst commented 1 year ago

Awesome, happy to help @devMagno! Let me know if you have further questions. For now, I'll close this issue. Thanks!