React Hooks-与多个动态组件共享父状态

时间:2020-07-02 08:42:29

标签: reactjs

我目前正在使用Hooks构建React应用。 我有一个组件( View ),该组件可以从某处加载数据并动态生成组件( Clicker,ClickerDisplay ),一方面显示数据,另一方面显示数据替换数据。

该应用程序基本上可以运行,但我担心的是它的性能。我的状态每100毫秒更新一次,如果更改后所有内容都重新呈现,那将是一个很大的开销。

是否可以仅重新渲染应更改的组件?

对我来说,另一件事似乎很丑陋,那就是我更新状态的方式。 setState([...state]);有更好的方法吗?

旁注:

在我的示例代码ID中,当前仅将项目属性传递给子组件,但我打算将整个项目传递给它们。

我还想保留父组件中的主要状态,因为应将应用程序的状态构建并保存到一个大的JSON对象中。

数据

//Some Random Data
const things = [
    {
        "id": 0,
        "name": "thing 1",
    },
    {
        "id": 1,
        "name": "thing 2",
    },
    {
        "id": 2,
        "name": "thing 3",
    }
];

View.js

export default function View(props) {

    const tmp = things.map(item => {
        return { ...item, amount: 0 };
    });

    const [state, setState] = useState(tmp);

    //alternates the amount and updates the state
    function updateAmount(item, newAmount) {
        item.amount = newAmount;
        setState([...state]);
    }

    function createClicker(item) {
        const { name, amount } = item;
        return (<Clicker
            name={name}
            amount={amount}
            clicked={() => updateAmount(item, amount + 1)} />
        );
    }

    function createClickerDisplay(item) {
        const { name, amount } = item;
        return (<ClickerDisplay
            name={name}
            amount={amount} />
        );
    }

    return (
        <ul>
            {state.map(item =>
                <li>
                    {createClicker(item)} = {createClickerDisplay(item)}
                </li>
            )}
        </ul>
    );

}

Clicker.js

//component to alternate data
function Clicker(props) {
    const { clicked, name, amount } = props;
    return <button onClick={() => clicked(name)}>{name}: {amount}</button>;
}

ClickerDisplay.js

//component to display data
function ClickerDisplay(props) {
    const { name, amount } = props;
    return <span>{name}: {amount}</span>;
}

1 个答案:

答案 0 :(得分:1)

除非配置为根据状态和道具的特定更改有条件地重新渲染,否则反应组件将在每个生命周期中重新渲染。在您的情况下,当您使用功能组件时,可以利用React.memo()来记住这些组件,这意味着只有在传递的prop内的值发生更改时才会呈现。尽管要注意复杂的结构,但是要小心,默认比较器只会做浅比较。

下面的代码段说明了记忆化组件的用法,以及解决了您作为点击事件处理程序一部分执行的状态突变。

const things = [
    { "id": 0, "name": "thing 1" },
    { "id": 1, "name": "thing 2" },
    { "id": 2, "name": "thing 3"}
];

function Clicker({ clicked, name, amount }) {
    return <button onClick={() => clicked(name)}>{name}: {amount}</button>;
}

// Memoized component that only updates when its simple props change
const ClickerDisplayMemoized = React.memo(function ClickerDisplay(props) {
    const { name, amount, index } = props;
    console.log(`Updating ClickerDisplay ${index + 1}`);
    return <span>{name}: {amount}</span>;
});

function View() {
    const thingsWithAmounts = things.map((item) => ({ ...item, amount: 0 }));
    const [state, setState] = React.useState(thingsWithAmounts);

    // Updated callback that avoids state mutation
    function updateAmount(index, newAmount) {
        setState(state.map((item, i) => ({
            ...item,
            amount: (i === index ? newAmount : item.amount)
        })));
    }

    const createClicker = ({ name, amount }, index) => (
        <Clicker name={name} amount={amount}
                 clicked={() => updateAmount(index, amount + 1)} />
    );
    const createClickerDisplay = ({ name, amount }, index) => (
        <ClickerDisplayMemoized name={name} amount={amount} index={ index } />
    );

    return (
        <ul>
            {state.map((item, i) => (
                // this component needs a key as it's part of a map()
                <li key={ i }>
                    {createClicker(item, i)} = {createClickerDisplay(item, i)}
                </li>
            ))}
        </ul>
    );
}

ReactDOM.render(<View />, document.getElementById('root'));
/* this is just so the console in this snippet doesn't cover the components */
.as-console-wrapper { max-height: 50% !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>