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

时间:2020-10-21 02:10:38

标签: javascript reactjs react-props use-context

我是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)
  }, [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)

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

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

1 个答案:

答案 0 :(得分:2)

如果您查看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>