joost
joost

Reputation: 149

Updating state is rerendering components which don't rely on this state

I have this very minimal app. It consist of one input element and a table. When pressing on a cell in the table, the input element shows what cell was pressed in the table. However, I noticed that it takes surprisingly long (0.3s) to show the selected cell in the input element. I then did some profiling, and console.logging, and this was the output:

enter image description here enter image description here

I'm still very much a beginner in React, and would like to know what design patterns I'm overseeing for functionality like this. I don't understand why the entire app is re-rendered (at least in the virtual DOM), even though only a small section of it (the <ShowSelectedCell/>) should change.

Minimal example: Index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Tooltip, TextField } from '@mui/material';
import { useState } from 'react'

const ShowSelectedCell = ({ cell }) => {
    return (
        <TextField disabled value={cell} />
    );
}

const SelectCell = ({ cells, onCellClick }) => {
    return (
        <table >
            <tbody>
                {console.log('rendering SelectCell')}
                {cells.map(function (template_name, r) {
                    return (
                        <tr key={r}>
                            {template_name.map(function (item, c) {
                                return (
                                    <Tooltip disableInteractive key={`${r} ${c} tooltip`} title={`${r} ${c}`} placement="bottom-start">
                                        <td style={{ "border": "solid" }}
                                            onClick={() => { onCellClick(r, c) }}
                                            key={`${r} ${c} button`}>
                                            {`${r} ${c}`}
                                        </td>
                                    </Tooltip>
                                );
                            })}
                        </tr>
                    );
                })}
            </tbody>
        </table>
    );
}

const App = () => {
    const [cells, setCells] = useState(Array(30).fill().map(() => Array(25).fill(null)))
    const [cell, setCell] = useState('')

    const onCellClick = (r, c) => {
        setCell([r, c])
        console.log(`${r}, ${c}`)
    }

    return (
        <div className='container'>
            {console.log('rerendering app')}
            <ShowSelectedCell cell={cell} />
            <SelectCell onCellClick={onCellClick} cells={cells} />
        </div>
    )
}

ReactDOM.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
    document.getElementById('root')
);

Index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />

    <title>React App</title>
</head>

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
</body>

</html>

Upvotes: 2

Views: 116

Answers (1)

Always Learning
Always Learning

Reputation: 5581

Your state variable is at the top level so when it changes that entire React component gets re-rendered. That means not only the ShowSelectedCell but the SelectCell table also. To get the behaviour you want, ShowSelectedCell would need to be the place with the state, and a way to have that state changed from outside.

This is most commonly handled using a Redux store. Having Redux in the mix allows a central place for data like state (which cell was selected) that can be modified from any component, and notified into any component wishing to show it.

So your choices are to either keep things as they are with the entire "container" div being re-rendered upon a cell click, or use Redux to allow the flow of information to be independent and only the affected React components to be rendered upon change.

When using Redux, there are React hooks (like useSelector) that are similar to the useState so that you can get the data from Redux in your ShowSelectedCell component.

Upvotes: 1

Related Questions