Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Child already has a parent, it must be removed first" when i'm reordering a table in react

I'm trying to ordenate a material-react-table in react.

In the column that i need to ordenate, i put this function:

<TableCell onClick={() => sortBy('login')}>Email</TableCell>

const sortBy = (key) => {
    // If is using a field different by the last, starts ordering in asc
    if (key !== orderingField) {
        setOrderingStatus('asc')
        setOrderingField(key)
    // Reverte a ordenação
    } else { 
        if (orderingStatus === 'asc') {
            setOrderingStatus('desc')
        } else {
            setOrderingStatus('asc')
        }
    }
    let copyUsers = {}
    copyUsers.data = [...users] // make a copy of the obj
    copyUsers.lastPage = JSON.parse(JSON.stringify(lastPage)) // copy of the last page 
    copyUsers.data.sort(compareValues(orderingField, orderingStatus)) // make the sort)
    dispatch(userActions.setUserList(copyUsers))
}

This is my function that ordering:

export default function compareValues(key, order = 'asc') {
    return function innerSort(a, b) {
      if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
        // property doesn't exist on either object
        return 0;
      }

      const varA = (typeof a[key] === 'string')
        ? a[key].toUpperCase() : a[key];
      const varB = (typeof b[key] === 'string')
        ? b[key].toUpperCase() : b[key];

      let comparison = 0;
      if (varA > varB) {
        comparison = 1;
      } else if (varA < varB) {
        comparison = -1;
      }
      return (
        (order === 'desc') ? (comparison * -1) : comparison
      );
    };
}

When i see in my reducer the request payload is comming like the expected, so, the ordering is been done, but this don't render again my table and i get some errors:

Child already has a parent, it must be removed first.

nbind.js:9812 Uncaught abort()

I'm using this redux useSelector() to get the value from of my table from the store:

const users = useSelector(state => state.userStates.users)

So in my table i show iterating by:

{users && users.map(user => (

Someone can show me why this is happening?

After a very hard debug, i discovered that the problem is something in the copy of the obj. When i do a copy with reference, like:

copyUsers.data = users

Everything works, but when i try a copy without reference, like:

copyUsers.data = JSON.parse(JSON.stringify(users))

or

copyUsers.data = [...users]

I receive the error.

Someone can explain this?

My package.json dependencies:

"dependencies": {
    "@material-ui/core": "4.9.5",
    "@material-ui/icons": "^4.9.1",
    "@material-ui/lab": "^4.0.0-alpha.45",
    "@react-pdf/renderer": "^1.6.8",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "axios": "^0.19.2",
    "formik": "^2.1.4",
    "immer": "^6.0.1",
    "notistack": "^0.9.8",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "react-export-excel": "^0.5.3",
    "react-history": "^0.18.2",
    "react-html2pdf": "^1.0.1",
    "react-input-mask": "^2.0.4",
    "react-redux": "^7.2.0",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.4.0",
    "redux": "^4.0.5",
    "redux-saga": "^1.1.3",
    "styled-components": "^5.0.1",
    "yup": "^0.28.1"
  },

My pdf component:

<Grid item>
    <UsersPDF />
</Grid>

This is my UsersPDF:

class UsersPDF extends Component {

    constructor(props) {
        super(props);
        this.state = {
            ready: false
        };
    }

    componentDidMount() {
        this.setState(
            () => ({
                ready: false
            }),
            () => {
                setTimeout(() => {
                    this.setState({ ready: true });
                }, 1);
            }
        );
    }

    render() {
        const { users } = this.props;
        const { ready } = this.state;

        const doc = (
            <Document>
                <Page style={styles.body}>
                    <View style={styles.rowCabecalho}>
                        <Text style={styles.nomeRelatorio}>Relatório de usuários</Text>
                    </View>
                    <View style={styles.table}>
                        <View style={styles.tableRow}>
                            <View style={styles.tableCol}>
                                <Text style={styles.tableCell}>Email</Text>
                            </View>
                            <View style={styles.tableCol}>
                                <Text style={styles.tableCell}>
                                    Filial logada
                                </Text>
                            </View>
                            <View style={styles.tableCol}>
                                <Text style={styles.tableCell}>Ativo</Text>
                            </View>
                        </View>
                        {users && users.map(user => (
                        <View key={user.id} style={styles.tableRow}>
                            <View style={styles.tableCol}>
                                <Text style={styles.tableCell}>{user.login}</Text>
                            </View>
                            <View style={styles.tableCol}>
                                <Text style={styles.tableCell}>{user.company_name}</Text>
                            </View>
                            <View style={styles.tableCol}>
                                <Text style={styles.tableCell}>
                                    {user.inactive === true ? 'Não' : 'Sim'}
                                </Text>
                            </View>
                        </View>
                        ))}
                    </View>
                </Page>
            </Document>
        );

        return (
            <>
                {ready && (
                    <PDFDownloadLink style={{textDecoration: 'none'}} className={styles.PDFDownloadLink} document={doc} fileName="usuarios.pdf">
                        {({ blob, url, loading, error }) =>
                            loading ? <></> : <Button variant="contained" color="primary">PDF</Button>
                        }
                    </PDFDownloadLink>
                )}
            </>
        );
    }
}

const mapStateToProps = state => ({
    users: state.userStates.users
});

export default connect(mapStateToProps)(UsersPDF);
like image 622
bla Avatar asked Mar 12 '20 12:03

bla


People also ask

Is it hard to make tables sortable in react?

Making your tables sortable in React might sound like a daunting task, but it doesn’t have to be too difficult. In this article, we’re going to implement all you need to sort out all of your table sorting needs. Table sorting has always been a pretty hard issue to get right.

How do I change what field we sort by in react?

To change what field we sort by, we need to remember the currently sorted field. We’ll do that with the useState hook. A hook is a special kind of function that lets us “hook” into some of React’s core functionality, like managing state and triggering side effects.

What is react-table?

Let's dive straight in. React Table is one of the most common table libraries in the React ecosystem. The react-table library is extremely lightweight and includes all of the essential functionality for every simple table. React-table is one of our favorites because it's simple to set up, customize, and expand.

Can I add my own features to react-table?

Using custom plugin Hooks, you can add your own features to the library. Use cases for react-table: For simple tables that include only the most basic functions such as scanning, sorting, and filtering. Some of the basic features are: Uncompromised user experience and interface. Within the table UI, there is clear typography and custom components.


1 Answers

EDIT 2: This is a known error there are some workarounds on this issue.

I am fairly sure that the error is in another component, this github issue (on @react-pdf/renderer) seems to be the same one you are having. They say it works the first time but throws the Child already has a parent, ... error after that.

What (I think) is happening is when state.userStates.users gets updated, the component that uses @react-pdf/renderer gets updated and leaks memory causing the error.

This doesn't happen when you don't clone the array because redux/react doesn't register it as a state change so the component doesn't re-render.

Solution from @willywill on github

const renderToStream = async function (element) {
  const instance = pdf(element);
  const buffer = await instance.toBuffer(); 
  instance.container.finish(); // This cleans up objects from memory (ADD ME)
  return buffer;
};

Edit: If you are using renderToStream make sure to use @react-pdf/renderer >=1.5.6 as it fixes this bug. (bug fix, 1.5.6 release)

like image 60
Trobol Avatar answered Nov 09 '22 05:11

Trobol