Как переместить состояние за пределы компонента с помощью поставщика контекста - PullRequest
0 голосов
/ 06 мая 2020

В настоящее время у меня есть компонент предварительного просмотра, к которому добавлена ​​функция перезагрузки с помощью хука useState. Теперь мне нужна возможность обновить sh этот компонент с той же функциональностью, но с внешним компонентом. Я знаю, что этого можно достичь с помощью 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} />
}

Итак, теперь я хочу просто иметь возможность импортировать компонент предварительного просмотра и иметь возможность обновлять sh его с помощью внешней кнопки, поэтому не использовать тот, который уже находится на <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>
    );
}

Так что да, как я сказал в закомментированном блоке, он будет работать, если поместить внешний кнопка там, однако тогда это делает его прикрепленным / привязанным к самому компоненту предварительного просмотра, я действительно не уверен, как передать состояние перезагрузки вне предварительного просмотра в поставщик. Может ли кто-нибудь указать, что мне не хватает и что мне нужно сделать, чтобы все работало так, как я хочу.

1 Ответ

1 голос
/ 06 мая 2020

Все, что вам нужно сделать, это написать собственный компонент PreviewProvider и сохранить его в состоянии перезагрузки и функцию toggleReload. Preview и previewControls могут использовать его с помощью context

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>
...