反应useContext不会将值传递给深层嵌套的子级

问题描述

我是useContext钩子上的新手,试图弄清楚我做错了什么...如果没有顶级导入,这就是我的代码

所以我项目的根目录如下:

export default function App() {

  return (
    <div className="App">
      <AppProvider>
        <Header/>
        <Toolbar/>
        <Canvas/>
        <Imports/>
        <Footer/>
      </AppProvider>
    </div>
  );
}

然后我有一个名为AppProvider的单独文件,该文件具有以下内容

const CanvasItems = createContext();
const UpdateCanvasItems = createContext();

export function useCanvasItems() {
  return useContext(CanvasItems)
}
export function useChangeItems() {
  return useContext(UpdateCanvasItems)
}

export default function AppProvider({children}) {
  const [state,setState] = useState({
    imports: [],images: [],text: [],circles: [],rectangles: [],draw: [],})

  // function pushes new items in to state object
  function addCanvasItem(type,source) {
    if (type === 'import') {
      const tempState = [...state.imports];
      tempState.push({id: Date.Now(),image: source});
      setState({...state,imports: tempState})

    } else if (type === 'image') {
      const tempState =  [...state.images];
      tempState.push({
        id: Date.Now(),src: source.src,x: window.innerWidth * Math.random(),y: window.innerHeight * Math.random(),})
      setState({...state,images: tempState})
    }
    console.log("state after new item added:",state)
  }

  return (
    <CanvasItems.Provider value={state}>
        <UpdateCanvasItems.Provider value={addCanvasItem}>
          {children}
        </UpdateCanvasItems.Provider>
    </CanvasItems.Provider>
  )
}

state对象和addCanvasItems函数传递到第一级子级(例如Header,Toolbar,Canvas等)时,我没有任何问题,但是在这些子级中有子级无法访问值的组件。

为清楚起见-在Canvas组件中,我有一个Stage组件,在其中有一个Layer组件,在其中有一个Items组件(我是使用Konva,并且需要进行所有嵌套)。

export default function Canvas() {
  const { onDrop,onDragOver } = useDragDrop;
  const state = useCanvasItems();

  useEffect(() => {
    console.log("state in canvas",state)
  },[state])
  
  return (
      <div id='canvas'>
        <Stage
          container='canvas'
          onDrop={onDrop}
          onDragOver={onDragOver}
        >
          <Layer>
            <Items/>
          </Layer>
        </Stage>
      </div>
  )
}

我需要访问state组件中的Items对象,但是我想避免道具钻探。为了进行测试,我创建了一个useEffect函数来在每次更改状态时打印状态。 Items组件在第一次加载时打印undefined,而Canvas组件则打印整个对象。

export default function Items() {
  const state = useCanvasItems();

  useEffect(() => {
    console.log("state in items",[state])

  return (
    <>
      {state && state.images.map(image => {
          return <NewImage image={image} />
      })}
    </>
  )
};

what prints to the console on load and after a file is imported (changing the state)

这个钩子我缺少什么以及如何实现?

非常感谢您的帮助!! ????

解决方法

如果您查看Stage的返回值,它不会呈现children,这意味着“图层”和“项目”被视为特殊情况。我认为您最好的选择是将道具传递给他们,而不是依赖于上下文,因为从技术上讲,它们似乎根本不是React渲染上下文的一部分,而是Konva自定义渲染器的一部分:KonvaRenderer

https://github.com/konvajs/react-konva/blob/master/src/ReactKonvaCore.js#L89

  // Stage component
  render() {
    const props = this.props;

    return (
      <div
        ref={(ref) => (this._tagRef = ref)}
        accessKey={props.accessKey}
        className={props.className}
        role={props.role}
        style={props.style}
        tabIndex={props.tabIndex}
        title={props.title}
      />
    );
  }

文档中有关于如何正确执行此操作的说明:

https://github.com/konvajs/react-konva#usage-with-react-context

由于React的已知问题,无法通过react-konva Stage组件的子级访问上下文。如果需要从舞台内预订上下文,则需要通过将提供者创建为舞台的子级来“桥接”上下文。有关更多信息,请参见此讨论和此react-redux演示。这是桥接上下文的示例(实时演示):

尝试再次将提供者添加为Stage的子代:

<Stage
  container='canvas'
  onDrop={onDrop}
  onDragOver={onDragOver}
>
  <CanvasItems.Provider value={state}>
    <UpdateCanvasItems.Provider value={addCanvasItem}>
      <Layer>
        <Items />
      </Layer>    
    </UpdateCanvasItems.Provider>
  </CanvasItems.Provider>
</Stage>