reactjs-更新状态会导致延迟

uxh89sit  于 2021-09-08  发布在  Java
关注(0)|答案(1)|浏览(473)

描述
我有一个使用 useCallback() 钩子来更新状态,但每次我这样做,都会导致页面延迟。我指的是实际的滞后,而不仅仅是“等待异步” setState() “延迟。
我的理论是更新状态会重新渲染太多的组件,因为如果我减少状态的值,延迟就会消失。
从本质上说,我担心我更新状态的方式没有隔离我想要的值。
代码
我将回购协议加载到一个代码沙盒中:https://codesandbox.io/s/long-forest-y9cdj?file=/src/app.js
要点如下:
页面上的按钮有一个onclick,从 App.js 在里面 App.js ,我们生成 handleClick 调用减速机的回调函数
在里面 reducer.js 这个 "UPDATE_COUNT" case更新状态以设置正确的值。
重现问题
乱动一下,以一种高节奏点击一些按钮,你应该会遇到一些滞后。你知道,这种滞后会导致按钮动画冻结,光标停留在“指针-手指”模型中。
问题
状态更新是否可以减少这种延迟?

k10s72fa

k10s72fa1#

我发现唯一奇怪的是你在申报你的 useStyles 钩住组件内部,以便在每个渲染周期创建新的样式。将这些移动到组件外部。
例子:
app.js

const useStyles = makeStyles((theme) => ({
    root: {
        flexGrow: 1
    },
    paper: {
        padding: theme.spacing(0),
        textAlign: "center",
        color: theme.palette.text.primary,
        backgroundColor: theme.palette.background
    }
}));

function App() {
  const classes = useStyles();

  const { state, dispatch } = React.useContext(MainContext);

  const handleClick = useCallback(
    (areaName, monsterName, newCount) => {
      newCount = newCount > 10 ? 10 : newCount;
      newCount = newCount < 0 ? 0 : newCount;

      dispatch({
        type: "UPDATE_COUNT",
        area: areaName,
        monster: monsterName,
        count: newCount
      });
    },
    [dispatch]
  );

  const areaObjects = Object.entries(state).map(([area, monster]) => {
    const monsters = Object.entries(monster).map(([monsterName, count]) => (
      <Grid item xs={12} key={monsterName}>
        <Monster
          area={area}
          name={monsterName}
          count={count}
          handleClick={handleClick}
        />
      </Grid>
    ));

    return (
      <Grid item xs={12} sm={6} md={4} lg={3} xl={2} key={area}>
        <Paper className={classes.paper}>
          <Grid item xs={12}>
            <h3>{area}</h3>
          </Grid>
          {monsters}
        </Paper>
      </Grid>
    );
  });

  return (
    <div className={classes.root} id={"app-container"} key={"app-container"}>
      <Grid container spacing={0}>
        {areaObjects}
      </Grid>
    </div>
  );
}

monster.js

const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1
  },
  addButtonContainer: {
    backgroundColor: "lightblue"
  },
  subtractButtonContainer: {
    backgroundColor: "lightyellow"
  },
  maxButtonContainer: {
    backgroundColor: "lightgreen"
  },
  monsterName: {
    textAlign: "center"
  },
  row: {
    backgroundColor: "lightgray"
  },
  rowSuccess: {
    backgroundColor: "lightgreen"
  }
}));

export default function Monster({ area, name, count, handleClick }) {
  const handleIncrement = (areaName, monsterName) => {
    handleClick(areaName, monsterName, count + 1);
  };

  const handleDecrement = (areaName, monsterName) => {
    handleClick(areaName, monsterName, count - 1);
  };

  const handleMaxout = (areaName, monsterName) => {
    handleClick(areaName, monsterName, 10);
  };

  const classes = useStyles();
  const rowClass = count === 10 ? classes.rowSuccess : classes.row;

  return (
    <Grid container item className={rowClass}>
      <Grid item xs={7} className={classes.monsterName}>
        {name}
      </Grid>
      <Grid item xs={1} className={classes.maxButtonContainer}>
        <IconButton
          color={"primary"}
          variant={"contained"}
          size={"small"}
          onClick={() => handleMaxout(area, name)}
        >
          <ArrowUpward fontSize={"small"} />
        </IconButton>
      </Grid>
      <Grid item xs={1} className={classes.addButtonContainer}>
        <IconButton
          color={"primary"}
          variant={"contained"}
          size={"small"}
          onClick={() => handleIncrement(area, name)}
        >
          <Add fontSize={"small"} />
        </IconButton>
      </Grid>
      <Grid item xs={2}>
        {count}
      </Grid>
      <Grid item xs={1} className={classes.subtractButtonContainer}>
        <IconButton
          color={"secondary"}
          variant={"contained"}
          size={"small"}
          onClick={() => handleDecrement(area, name)}
        >
          <Remove fontSize={"small"} />
        </IconButton>
      </Grid>
    </Grid>
  );
}

我还记下了 state 由上下文提供,尽管我不完全认为这一部分是必要的。

index.js

import React, { useMemo } from "react";

import initialState from "./initialState";
import reducer from "./reducer";

const MainContext = React.createContext();

function MainContextProvider(props) {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  const value = useMemo(() => ({ state, dispatch }), [state]);

  return (
    <MainContext.Provider value={value}>{props.children}</MainContext.Provider>
  );
}

const MainContextConsumer = MainContext.Consumer;

export { MainContext, MainContextProvider, MainContextConsumer };

相关问题