在内部组件中使用钩子

Using hooks in inner Component

我正在尝试使用 Hook 创建组件,但我遇到了一个奇怪的问题。
我在我的代码中使用 mapbox-gl。为了初始化 mapbox-gl,我必须等到 dom 组件加载完毕。 (useLayoutEffect 或 useEffect)
初始显示没有问题,但是当我按下按钮(L72)时,由 mapbox-gl 创建的 canvas 被卸载,没有控制台错误。
我尝试将MyMap组件移到Tile组件(L35-L45)之外,然后没有发生上述问题。

我是不是用错了 Hook?

我的示例完整代码如下。
[CodeSandbox]

这是Map.tsx中的节选:

export const Tile: React.FunctionComponent<PropsType> = ({
  mapComponentLoaded,
  trigger,
  triggered
}) => {
  const classes = useStyles({});

  React.useLayoutEffect(() => { // or useEffect
    // init mapbox-gl
    mapComponentLoaded();
  }, []); // run at once

  // it doesn't works. if you clicked the button, the canvas under div#map would unmount.
  const MyMap = (props: { triggered: boolean }) => (
    <Paper className={classes.content}>
      <div id="map" className={classes.map} />
      <Typography>{props.triggered ? "fired" : "not fired"}</Typography>
    </Paper>
  );

  return (
    <div className={classes.root}>
      <Grid container spacing={1} className={classes.root}>
        <Grid item xs={12}>
          <Button variant="contained" color="primary" onClick={() => trigger()}>
            Add Boundary
          </Button>
        </Grid>
        <Grid item xs={12} className={classes.map}>
          <MyMap triggered={triggered} />
        </Grid>
      </Grid>
    </div>
  );
};

谢谢。

当你在一个功能组件中定义一个组件时,在每次渲染时都会创建一个新的引用,因此,react 不会重新渲染,而是重新挂载它已创建新组件

当您将组件从功能组件中取出时,函数的引用不会改变,因此 React 会正确呈现它

现在另一种可行的方法是,如果不是将 MyMap 作为组件呈现,而是将其作为函数调用。

export const Tile: React.FunctionComponent<PropsType> = ({
  mapComponentLoaded,
  trigger,
  triggered
}) => {
  const classes = useStyles({});

  React.useLayoutEffect(() => { // or useEffect
    // init mapbox-gl
    mapComponentLoaded();
  }, []); // run at once

  // it doesn't works. if you clicked the button, the canvas under div#map would unmount.
  const MyMap = (props: { triggered: boolean }) => (
    <Paper className={classes.content}>
      <div id="map" className={classes.map} />
      <Typography>{props.triggered ? "fired" : "not fired"}</Typography>
    </Paper>
  );

  return (
    <div className={classes.root}>
      <Grid container spacing={1} className={classes.root}>
        <Grid item xs={12}>
          <Button variant="contained" color="primary" onClick={() => trigger()}>
            Add Boundary
          </Button>
        </Grid>
        <Grid item xs={12} className={classes.map}>
          {MyMap({triggered})}
        </Grid>
      </Grid>
    </div>
  );
};

Working demo with second approach

P.S. However taking the definition out of function definition is a much better approach as it gives you the flexibility to add more performance optimizations using React.memo and also to use hooks within this component