简体   繁体   中英

MUI-Datatable, child component detecting change in the state of the parent component

I'm starting with React and this problem has taken my sleep away.

From what I understand, the state (this.state) is only accessible in the component in which it was created, but in my project this is not happening. A child component is able to identify changes made to the state of the parent component, and this has slowed my application, let me try to explain it better with the code:

In the parent component, I render the page title, a modal for adding new records and include the child component responsible for assembling a datatable (provided by MUI-Datatable and data loaded via API):

https://github.com/gregnb/mui-datatables

Parent component:

import React from 'react';
import Typography from '@material-ui/core/Typography';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import Button from '@material-ui/core/Button';

// Components
import CadcliFieldContent from './CadcliFieldContent';

class CadcliField extends React.Component {

    state = {
        isLoading: false,
        isNew: true,
        dialog: false,
        formData: {
            name: 'Test'
        }
    };

    openDialog = () => { this.setState({ dialog: true }); }
    closeDialog = () => { this.setState({ dialog: false }); }
    formDataChange = name => event => {        
        var formDataTemp = this.state.formData;      
        formDataTemp[name] = event.target.value;
        this.setState({ formData: formDataTemp });
    };

    render () {

        return (
            <div className="flex flex-1 w-full">

                <div className="flex items-center justify-end">
                    <Button
                        className="normal-case"
                        variant="contained" 
                        color="secondary"
                        role="button"
                        onClick={this.openDialog}
                    >
                        <Icon>add</Icon>
                        <span className="mx-1">{t('NEW_FIELD')}</span>
                    </Button>
                </div>
                            
                <CadcliFieldContent 
                    urlBase="cadcli-field/" 
                />
                
                <Dialog
                    className='max-w-lg w-full m-24'
                    onClose={this.closeDialog}
                    open={this.state.dialog}
                >
                    <DialogTitle component="div" className="p-0">
                        <Typography className="text-16 ml-8">Fields</Typography>
                    </DialogTitle>
                    <DialogContent className="p-16 sm:p-24">
                        <div className="flex items-center mb-24">
                            <TextField
                                type="text"
                                name="name"
                                id="input-name"
                                value={formData.name}
                                onChange={this.formDataChange('name')}
                                variant="outlined"
                                fullWidth
                                required
                            />
                        </div>
                    </DialogContent>

                </Dialog>
            </div>
        );
    };
}

export default CadcliField;

Child component

import React from 'react';
import axios from 'axios';
import MUIDataTable from "mui-datatables";
import { CircularProgress, Typography } from '@material-ui/core';

class CadcliContent extends React.Component {
    state = {
        urlApi: process.env.REACT_APP_API_URL, 
        page: 0,
        count: 1,
        rowsPerPage: 10,
        denseTable: true,
        idDeleteModal: 0,
        sortOrder: {},
        filter: {},
        search: '',
        data: [['Loading...']],
        columns: [
            {name: 'id', label: this.props.t('utils:ACTIONS')},
            {name: 'id', label: 'ID', options: { filterType : 'textField' }},           
        ],
        isLoading: false,
    };

    componentDidMount() {
        this.getData(this.state.urlApi, 0);
    }

    // get data
    getData = async (url, page) => {
        this.setState({ isLoading: true });
        const res = await this.axiosRequest(url, page);
        this.setState({ data: res.data, isLoading: false, count: res.total });
    };

    axiosRequest = (url, page, sortOrder = {}, filter = {}, searchText = '') => {

        url = url + '?page_size=' + this.state.rowsPerPage;
        url += '&page=' + (page + 1);   

        // Ordering
        if (sortOrder.name !== undefined) {
            if (sortOrder.direction === 'asc') {
                url += '&ordering=' + sortOrder.name;
            } else {
                url += '&ordering=-' + sortOrder.name;
            }
        }

        // Filtering
        if (filter.length > 0) {
            var columnsTable = this.state.columns;
            filter.forEach(function(value, key){
                if (value.length > 0) {
                    url += '&' + columnsTable[key].name + '=' + value;
                }
            });
        }

        // Generic Searching
        if (searchText !== "" && searchText !== null) {
            url += '&search=' + searchText;
        }
        
        return new Promise((resolve, reject) => {
            axios
            .get(url)
            .then(response => {
                resolve({
                    data: response.data.results,
                    page: page,
                    total: response.data.count,
                });
            }).catch(function (error) {
                resolve({
                    data: [],
                    page: 0,
                    total: 0,
                });
            });
        });
    };

    sort = (page, sortOrder, filter) => {
        this.setState({ isLoading: true });
        this.axiosRequest(this.state.urlApi, page, sortOrder, filter).then(res => {
            this.setState({
                data: res.data,
                page: res.page,
                sortOrder,
                filter,
                isLoading: false,
                count: res.total,
            });
        });
    };
        
    changePage = (page, sortOrder, filter) => {
        this.setState({
            isLoading: true,
        });

        this.axiosRequest(this.state.urlApi, page, sortOrder, filter).then(res => {
            this.setState({
                isLoading: false,
                page: res.page,
                sortOrder,
                filter,
                data: res.data,
                count: res.total,
            });
        });
    };

    filterChange = (page, sortOrder, filter) => {
        this.setState({
            isLoading: true,
        });

        this.axiosRequest(this.state.urlApi, page, sortOrder, filter).then(res => {
            this.setState({
                isLoading: false,
                page: res.page,
                sortOrder,
                filter,
                data: res.data,
                count: res.total,
            });
        });
    };

    searchTable = (page, sortOrder, columnsFilter, searchText) => {
        this.setState({
            isLoading: true,
        });
        
        this.axiosRequest(this.state.urlApi, page, sortOrder, columnsFilter, searchText).then(res => {
            this.setState({
                isLoading: false,
                page: res.page,
                sortOrder,
                filter: columnsFilter,
                search: searchText,
                data: res.data,
                count: res.total,
            });
        });
        
    }

    render() {
    
        const { data, count, isLoading, rowsPerPage, sortOrder } = this.state;

        const options = {
            filter: true,
            filterType: 'dropdown',
            responsive: 'vertical',         
            serverSide: true,
            download: false,
            count: count,
            rowsPerPage: rowsPerPage,
            rowsPerPageOptions: [],
            sortOrder: sortOrder,
            resizableColumns: false,
            confirmFilters: true,               
            onTableChange: (action, tableState) => {    
                switch (action) {                   
                    case 'changePage':
                        this.changePage(tableState.page, tableState.sortOrder, tableState.filterList);
                        break;
                    case 'sort':
                        this.sort(tableState.page, tableState.sortOrder, tableState.filterList);
                        break;
                    case 'search':
                        this.searchTable(tableState.page, tableState.sortOrder, tableState.filterList, tableState.searchText);
                        break;
                    default:
                        console.log('[' + action + '] action not handled.');
                }
            },      
            setTableProps: () => {
                return {
                    size: this.state.denseTable ? 'small' : 'medium'
                };
            },
        };

        return (
            <div className="w-full flex flex-col">
                <MUIDataTable
                    title={
                        <Typography variant="h6">
                            Listing
                            {isLoading && <CircularProgress size={24} />}
                        </Typography>
                    }
                    data={data}
                    columns={this.state.columns}
                    options={options}
                />
            </div>  
        );
    
    }
}

export default CadcliContent;

What happens:

Every time I change the state in the parent component, for example, type in TextField and change the this.state.formData.name property, or click the button to open the modal (change this.state.dialog) the onTableChange function of the MUI Datatables is triggered and generates a processing in my table, I realize this because I put a console.log () to inform this:

在此处输入图像描述

onTableChange: (action, tableState) => {    
    switch (action) {                   
        /* .... */
        default:
            console.log('[' + action + '] action not handled.');
    }
},   

(Excerpt from the code where console.log is triggered)

This disturbs me because MUI-Datatable understands that it has changed something in this.state and does a very heavy processing, even leaving the processing of typing in the delayed input.

Any idea what might be going on?

The solution was simpler than I imagined, and I ended up solving it by studying and understanding the React state concept.

Even though I still didn't understand how the MUI-Datables component observed the entire state, the simple fact of encapsulating the Dialog inside a new component, with a new isolated state, the magic happened.

So if someone goes through the same problem the solution was this:

CadcliField.js

<CadcliFieldContent 
    urlBase="cadcli-field/" 
   {...this.props}
/>

<CadcliFieldFormNew 
    isNew={this.state.isNew}
    dialog={this.state.dialog}
    closeDialog={this.closeDialog}
/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM