上下文安装程序未更新状态



上下文总是让我困惑。我希望新的Context API会有所帮助,但到目前为止,仍然停滞不前。我可以获得要显示的初始值,但基于我的效果的更新不会使用useContext值更新组件。我做错了什么?

这里"需要"上下文太过分了,但我希望有一个上下文API,它给出窗口滚动位置的值,只是为了测试上下文提供者/消费者如何与儿童互动:

context.js

import React, { useState, FC, useEffect } from "react";
interface IInitialContext {
scrollY: number;
// do to - implement removeEventListener function
}
const intialContext = {
scrollY: window.scrollY,
// removeEventListener: () => {},
};
const ScrollContext = React.createContext<IInitialContext>(intialContext);
const VerticalScrollContextProvider: FC = ({ children }) => {
const [scrollY, setScrollY] = useState(intialContext.scrollY);
// correctly logs scrollY
useEffect(() => {
window.addEventListener(
"scroll",
debounce(() => setScrollY(window.scrollY), 100) // correctly captures new scrollY position
);
return () => window.removeEventListener("scroll", debounce)
}, [scrollY]); // re-add listener when scrollY changes
return (
<ScrollContext.Provider value={{ scrollY }}> //i don't really need the value passed to component
{children}
</ScrollContext.Provider>
);
};
function useScrollState() {
const context = React.useContext(ScrollContext);
if (context === undefined) {
throw new Error(
"useScrollState must be used within a VerticalScrollContextProvider"
);
}
return context; // correctly tracks initial state 
}
export { VerticalScrollContextProvider, useScrollState };

以及我的组件:

const Home = () => {
const { scrollY } = useScrollState();
// correctly has value on app first loading, does not receive updates.
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
console.log("clicked button");
};
return (
<VerticalScrollContextProvider>
<StyledHome>
<div id="text-wrapper">
<StyledFirstName>Phil</StyledFirstName>
<StyledLastName>Lucks</StyledLastName>
</div>
<Button text="clicker" onClick={handleClick} />
</StyledHome>
</VerticalScrollContextProvider>
);
};
export default Home;

我很欣赏任何真知灼见。

要使上下文工作,组件应该在提供程序内部

从useContext的文档

接受上下文对象(从React.createContext返回的值(并返回该上下文的当前上下文值。电流上下文值由最近值的值属性确定位于树中调用组件的上方。

当组件上方最近的更新时,该Hook将触发一个带有传递的最新上下文值的重新转发器到MyContext提供者。

现在密切关注<Home />组件。

const Home = () => {
const { scrollY } = useScrollState();
// correctly has value on app first loading, does not receive updates.
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
console.log("clicked button");
};
return (
<VerticalScrollContextProvider>
<StyledHome>
<div id="text-wrapper">
<StyledFirstName>Phil</StyledFirstName>
<StyledLastName>Lucks</StyledLastName>
</div>
<Button text="clicker" onClick={handleClick} />
</StyledHome>
</VerticalScrollContextProvider>
);
};

我们感兴趣的这条线是const { scrollY } = useScrollState();,它试图从父Provider导出context

但是,<Home />组件在<Provider />内部吗

没有。<Provider /><Home />组件内

运行下面的代码段并查看控制台日志,查看哪个组件首先运行。

const { useState, useEffect } = React;
const intialContext = {
scrollY: window.scrollY,
};
const ScrollContext = React.createContext(intialContext);
const VerticalScrollContextProvider = ({ children }) => {
const [scrollY, setScrollY] = React.useState(intialContext.scrollY);
// correctly logs scrollY
console.log("Rendering context");
useEffect(() => {
const eventListener = () => setScrollY(window.scrollY);
window.addEventListener(
"scroll",
eventListener// correctly captures new scrollY position
);
return () => window.removeEventListener("scroll", eventListener)
}, [scrollY]); // re-add listener when scrollY changes
return (
<ScrollContext.Provider value={{ scrollY }}>
{children}
</ScrollContext.Provider>
);
};
function useScrollState() {
const context = React.useContext(ScrollContext);
if (context === undefined) {
throw new Error(
"useScrollState must be used within a VerticalScrollContextProvider"
);
}
return context; // correctly tracks initial state 
}

const Home = () => {
const { scrollY } = useScrollState();
console.log("Rendering Home");
// correctly has value on app first loading, does not receive updates.
const handleClick = (e) => {
e.preventDefault();
console.log("clicked button");
};
return (
<VerticalScrollContextProvider>
<div id="text-wrapper">
<h3>Phil</h3>
<h3>Lucks</h3>
</div>
<button onClick={handleClick}>Button</button>
<h2>Scroll and see the console logs</h2>
</VerticalScrollContextProvider>
);
};
ReactDOM.render(<Home />, document.getElementById("root"));
#root {
height: 1000px;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

解决方案。。。

Home组件包裹在Provider

import React from "react";
import {VerticalScrollContextProvider, useScrollState} from "./context";
const Home = () => {
const { scrollY } = useScrollState();
// correctly has value on app first loading, does not receive updates.
const handleClick = (e) => {
e.preventDefault();
console.log("clicked button");
};
return (
<div>
<div id="text-wrapper">
<h3>Phil</h3>
<h3>Lucks</h3>
{scrollY}
</div>
<button text="clicker" onClick={handleClick} />
</div>
);
};
export default () => (
<VerticalScrollContextProvider><Home /></VerticalScrollContextProvider>
);

运行下面的代码段并滚动查看控制台日志。

const {
useState,
useEffect
} = React;
const intialContext = {
scrollY: window.scrollY,
};
const ScrollContext = React.createContext(intialContext);
const VerticalScrollContextProvider = ({
children
}) => {
const [scrollY, setScrollY] = React.useState(intialContext.scrollY);
// correctly logs scrollY
console.log("Rendering context");
useEffect(() => {
const eventListener = () => setScrollY(window.scrollY);
window.addEventListener(
"scroll",
eventListener // correctly captures new scrollY position
);
return () => window.removeEventListener("scroll", eventListener)
}, [scrollY]); // re-add listener when scrollY changes
return ( <
ScrollContext.Provider value = {
{
scrollY
}
} > {
children
} <
/ScrollContext.Provider>
);
};
function useScrollState() {
const context = React.useContext(ScrollContext);
if (context === undefined) {
throw new Error(
"useScrollState must be used within a VerticalScrollContextProvider"
);
}
return context; // correctly tracks initial state 
}

const Home = () => {
const {
scrollY
} = useScrollState();
console.log("Rendering Home", scrollY);
// correctly has value on app first loading, does not receive updates.
const handleClick = (e) => {
e.preventDefault();
console.log("clicked button");
};
return ( <
React.Fragment >
<
div id = "text-wrapper" >
<
h3 > Phil < /h3> <
h3 > Lucks < /h3> <
/div> <
button onClick = {
handleClick
} > Button < /button> <
h2 > Scroll and see the console logs < /h2> <
/React.Fragment>
);
};
const HomeWithProvider = () => {
return <VerticalScrollContextProvider >
<
Home / >
<
/VerticalScrollContextProvider>
}
ReactDOM.render( < HomeWithProvider / > , document.getElementById("root"));
#root {
height: 1000px;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

相关内容

  • 没有找到相关文章

最新更新