如何使用上下文提供程序将状态移出组件之外

时间:2020-05-05 21:49:06

标签: javascript reactjs react-hooks context-api

我目前有一个预览组件,该组件使用useState挂钩附加了重新加载功能。我现在希望能够使用相同的功能但使用外部组件来刷新此组件。我知道可以通过useContext API来实现,但是我正在努力将它们全部整合在一起。

上下文:

const PreviewContext = React.createContext({
    handleRefresh: () => null,
    reloading: false,
    setReloading: () => null
  });

const PreviewProvider = PreviewContext.Provider;

PreviewFrame:

const PreviewFrame = forwardRef((props, ref) => {
  const { height, width } = props;
  const classes = useStyles({ height, width });

  return (
    <Card className={classes.root} ref={ref}>
      <div className={classes.previewWrapper} > {props.children} </div>
      <div className={classes.buttonContainer}>
        <IconButton label={'Refresh'} onClick={props.toggleReload} />
      </div>
    </Card>
  );
});

PreviewFrameWrapped:

<PreviewFrame
   toggleReload={props.toggleReload}
   height={props.height}
   width={props.width}
   ref={frameRef}
>
  <PreviewDiv isReloading={props.isReloading} containerRef={containerRef} height={height} width={width} />
</PreviewFrame>

const PreviewDiv = ({ isReloading, containerRef, height, width }) => {
  const style = { height: `${height}px`, width: `${width}px`};
  return !isReloading ?
    <div className='div-which-holds-preview-content' ref={containerRef} style={style} />
    : null;
};

预览:

export default function Preview(props) {

  const [reloading, setReloading] = useState(false);

  useEffect(() => {
    setReloading(false);
  }, [ reloading ]);

  const toggleReload = useCallback(() => setReloading(true), []);

  return <PreviewFrame isReloading={reloading} toggleReload={toggleReload} {...props} />
}

所以现在我只想导入预览组件并能够使用外部按钮刷新它,而不要使用<PreviewFrame>上已经存在的组件。

我理想地想像这样食用它:

import { PreviewContext, PreviewProvider, Preview } from "../../someWhere"

<PreviewProvider>
    <Preview />
    <PreviewControls />
</PreviewProvider>

function PreviewControls () {
  let { handleRefresh } = React.useContext(PreviewContext);
  return <div><button onClick={handleRefresh}>↺ Replay</button></div>
}

预览与提供者的包装:


export default function Preview(props) {

  const [reloading, setReloading] = useState(false);

  useEffect(() => {
    setReloading(false);
  }, [ reloading ]);

  const toggleReload = useCallback(() => setReloading(true), []);

  return (<PreviewProvider value={{ reloading: reloading, setReloading: setReloading, handleRefresh: toggleReload }} >
            <PreviewFrame isReloading={reloading} toggleReload={toggleReload} {...props} />
             {/* it works if i put the external button called <PreviewControls>  here*/}
          </PreviewProvider>
    );
}

是的,就像我在注释掉的代码块中所说的那样,如果在其中放置一个外部按钮,它将可以正常工作,但是那样会使它附加/绑在Preview组件本身上,我真的不确定如何转移重新加载状态预览到提供者之外。有人可以指出我所缺少的是什么,我需要做些什么才能使其以我想要的方式工作。

1 个答案:

答案 0 :(得分:1)

所有您需要做的就是编写一个自定义组件PreviewProvider并存储在重新加载状态和在那里的toggleReload函数中。预览和PreviewControl可以使用上下文使用它

const PreviewContext = React.createContext({
    handleRefresh: () => null,
    reloading: false,
    setReloading: () => null
  });

export default function PreviewProvider({children}) {

  const [reloading, setReloading] = useState(false);

  useEffect(() => {
    setReloading(false);
  }, [ reloading ]);

  const toggleReload = useCallback(() => setReloading(true), []);

  return <PreviewContext.Provider value={{reloading, toggleReload}}>{children}</PreviewContext.Provider>
}

export default function Preview(props) {

  const {reloading, toggleReload} = useContext(PreviewContext);

  return <PreviewFrame isReloading={reloading} toggleReload={toggleReload} {...props} />
}

function PreviewControls () {
  let { toggleReload } = React.useContext(PreviewContext);
  return <div><button onClick={toggleReload}>↺ Replay</button></div>
}

最后像这样使用

import { PreviewContext, PreviewProvider, Preview } from "../../someWhere"

<PreviewProvider>
    <Preview />
    <PreviewControls />
</PreviewProvider>
相关问题