Reactjs React钩子-上下文提供程序未立即反映已调度状态
我的目标: 我正在创建一个向用户显示数据的表。基于存储在全局状态中的某个值(存储在上下文API提供给其他组件的reducer函数中),我将在scroll上将该标题固定到页面顶部,但仅当该表处于视图中时。为此,我必须注册一个on Scroll和on Resize事件侦听器,以便在用户滚动或调整屏幕大小时重新计算表的位置。仅当表在视图中且状态尚未设置为Reactjs React钩子-上下文提供程序未立即反映已调度状态,reactjs,state,react-hooks,react-context,use-reducer,Reactjs,State,React Hooks,React Context,Use Reducer,我的目标: 我正在创建一个向用户显示数据的表。基于存储在全局状态中的某个值(存储在上下文API提供给其他组件的reducer函数中),我将在scroll上将该标题固定到页面顶部,但仅当该表处于视图中时。为此,我必须注册一个on Scroll和on Resize事件侦听器,以便在用户滚动或调整屏幕大小时重新计算表的位置。仅当表在视图中且状态尚未设置为ishaderactivelyfixed:true时,我才想将全局状态更新为ishaderactivelyfixed:true。否则,每当表处于视图中
ishaderactivelyfixed:true时,我才想将全局状态更新为ishaderactivelyfixed:true
。否则,每当表处于视图中且用户滚动到ishaderactivelyfixed:true
时,我都会不断更新状态,同样,当它不在视图中时,也会滚动到ishaderactivelyfixed:false
问题:
我已经按照我认为需要的方式设置了上述场景。然而,当我分派到全局状态,然后控制台记录或使用该全局状态时,它并不反映我刚才分派给它的内容。react dev toolsDO显示我调度的更新状态,但我需要能够在我调度它的函数中更新新调度的状态。这样我就知道不要再发了。我希望这是有道理的。提前谢谢
代码:(注意:我去掉了不必要的代码,所以有些东西可能看起来很奇怪。我留下了一些代码来提供问题的上下文。我评论的区域就是问题产生的地方。isactiveyview()
函数只获取表getBoundingClientRect()
并检查其是否仍在视图中)
ProductTableStore.jsx
import React from 'react';
const initialState = {
isLoading: true,
isSelectable: null,
isHeaderFixedOnScroll: null,
isHeaderActivelyFixed: null,
isAddToCartEnabled: null,
productTableActiveWidth: null,
addToCartData: null,
};
const reducer = (state, action) => {
switch (action.type) {
case 'setIsHeaderFixedOnScroll':
return {
...state,
isHeaderFixedOnScroll: action.isHeaderFixedOnScroll,
};
case 'setIsHeaderActivelyFixed':
return {
...state,
isHeaderActivelyFixed: action.isHeaderActivelyFixed,
};
case 'setProductTableActiveWidth':
return {
...state,
productTableActiveWidth: action.productTableActiveWidth,
};
default:
throw new Error(
`Unexpected or missing action type. Action type provided was: ${action.type}`
);
}
};
const ProductTableContext = React.createContext({});
const ProductTableStore = () => {
return React.useContext(ProductTableContext);
};
const ProductTableProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<ProductTableContext.Provider value={[state, dispatch]}>
{children}
</ProductTableContext.Provider>
);
};
export { ProductTableStore, ProductTableProvider };
const ProductTable = ({ heading, ariaLabel, children }) => {
const [globalState, dispatch] = ProductTableStore();
const ProductTableRef = React.useRef(null);
const setIsHeaderActivelyFixed = (isHeaderActivelyFixed) => {
dispatch({
type: 'setIsHeaderActivelyFixed',
isHeaderActivelyFixed,
});
};
const setProductTableActiveWidth = (productTableActiveWidth) => {
dispatch({
type: 'setProductTableActiveWidth',
productTableActiveWidth: `${productTableActiveWidth}px`,
});
};
const useShouldHeaderBeFixed = (ref) => {
if (!globalState.isHeaderFixedOnScroll) return;
// keep mutable refs of values pertinent to the fixed header for the lifetime of the component
const fixedState = React.useRef(null);
const fixedWidth = React.useRef(null);
const [shouldHeaderBeFixed, setShouldHeaderBeFixed] = React.useState(false);
const calculateTablePosition = () => {
if (!fixedState.current && isActivelyViewed(ref.current)) {
setShouldHeaderBeFixed(true);
fixedState.current = true;
} else if (!!fixedState.current && !isActivelyViewed(ref.current)) {
setShouldHeaderBeFixed(false);
fixedState.current = false;
}
};
const calculateTableWidth = () => {
if (fixedWidth.current !== ProductTableRef.current.offsetWidth) {
setProductTableActiveWidth(ProductTableRef.current.offsetWidth);
fixedWidth.current = ProductTableRef.current.offsetWidth;
}
};
const calculateTablePositionAndWidth = () => {
calculateTablePosition();
calculateTableWidth();
};
React.useEffect(() => {
calculateTablePositionAndWidth();
}, []);
React.useEffect(() => {
window.addEventListener('scroll', calculateTablePosition);
window.addEventListener('resize', calculateTablePositionAndWidth);
return () => {
window.removeEventListener('scroll', calculateTablePosition);
window.removeEventListener('resize', calculateTablePositionAndWidth);
};
}, [isActivelyViewed(ref.current)]);
return shouldHeaderBeFixed;
};
// initiallize our custom hook
const shouldHeaderBeFixed = useShouldHeaderBeFixed(ProductTableRef);
// listen only to our custom hook to set global state for the fixed header
React.useEffect(() => {
setIsHeaderActivelyFixed(shouldHeaderBeFixed);
}, [shouldHeaderBeFixed, globalState.isHeaderFixedOnScroll]);
...
从“React”导入React;
常量初始状态={
孤岛加载:是的,
isSelectable:空,
isHeaderFixedOnScroll:null,
isHeaderActivelyFixed:null,
isAddToCartEnabled:空,
productTableActiveWidth:null,
addToCartData:null,
};
const reducer=(状态、操作)=>{
开关(动作类型){
案例“setIsHeaderFixedOnScroll”:
返回{
……国家,
isHeaderFixedOnScroll:action.isHeaderFixedOnScroll,
};
案例“SetIShaderActiveFixed”:
返回{
……国家,
isHeaderActivelyFixed:action.isHeaderActivelyFixed,
};
案例“setProductTableActiveWidth”:
返回{
……国家,
productTableActiveWidth:action.productTableActiveWidth,
};
违约:
抛出新错误(
`意外或缺少操作类型。提供的操作类型为:${action.type}`
);
}
};
const ProductTableContext=React.createContext({});
常量ProductTableStore=()=>{
返回React.useContext(ProductTableContext);
};
const ProductTableProvider=({children})=>{
const[state,dispatch]=React.useReducer(reducer,initialState);
返回(
{儿童}
);
};
导出{ProductTableStore,ProductTableProvider};
ProductTable.jsx(我遇到问题的文件)
从“React”导入React;
从“./ProductTableStore/ProductTableStore”导入{ProductTableStore};
从“../../js/helpers”导入{isactiveyview};
const ProductTable=({标题、标签、子项})=>{
const[globalState,dispatch]=ProductTableStore();
常量[isOnScrollResizeEventRegistered,setIsOnScrollResizeEventRegistered]=React.useState(
无效的
);
const ProductTableRef=React.useRef(null);
常量registerOnScrollResizeEvent=(参考,解析)=>{
console.log('registing onScrollandResize');
window.addEventListener(
“卷轴”,
_.节气门(()=>{
计算位置(ref);
}),
10
);
window.addEventListener(
“调整大小”,
_.节气门(()=>{
CalculateProductTableValue(参考);
}),
10
);
if(resolve)resolve();
};
常量setIsHeaderActivelyFixed=(isHeaderActivelyFixed)=>{
console.log('fx setishaderactivelyfixed.Passed参数:',ishaderactivelyfixed);
派遣({
类型:“SetIShaderActivelyFixed”,
我主动修正,
});
log('ishaderactivelyfixed',globalState.ishaderactivelyfixed);
//这返回null和idk为什么!我想可能是因为没有重新渲染,但是
//在下面的效果上更改回调似乎不会改变这一点
};
const setProductTableActiveWidth=(productTableActiveWidth)=>{
log('fx setProductTableActiveWidth');
派遣({
类型:“setProductTableActiveWidth”,
productTableActiveWidth:`${productTableActiveWidth}px`,
});
log('productTableActiveWidth',globalState.productTableActiveWidth);
//这返回null和idk为什么!我想可能是因为没有重新渲染,但是
//在下面的效果上更改回调似乎不会改变这一点
};
常量计算位置=(参考)=>{
如果(IsActive查看(参考当前)和&!globalState.IsHeaderActiveFixed){
SetIShaderActivelyFixed(真);
}如果(!isactiveyView(ref.current)和&globalState.ishaderactivelyfixed),则为else{
//这永远不起作用,因为globalState从未在该函数中反映更新
SetIShaderActiveFixed(假);
}否则{
console.log('这些都不是…');
}
};
常量计算宽度=(参考)=>{
if(ref.current.offsetWidth!==globalState.productTableActiveWidth){
setProductTableActiveWidth(参考当前偏移宽度);
}
};
const calculateProductTableValues=(ProductTableRef,resolve)=>{
计算宽度(ProductTableRef);
计算位置(ProductTableRef);
if(resolve)resolve();
};
React.useffect(()=>{
如果(!globalState.isHeaderFixedOnScroll)返回;
新承诺((解决、拒绝)=>{
如果(isOnScrollResizeEventRegis
const setIsHeaderActivelyFixed = (isHeader) => {
dispatch({
type: 'setIsHeaderActivelyFixed',
isHeaderActivelyFixed: isHeader
});
};
const initialState = {
isLoading: true,
isSelectable: null,
isHeaderFixedOnScroll: null,
isHeaderActivelyFixed: null,
isAddToCartEnabled: null,
productTableActiveWidth: null,
addToCartData: null,
};
const reducer = (state, action) => {
switch (action.type) {
case 'setIsHeaderFixedOnScroll':
return {
...state,
isHeaderFixedOnScroll: action.isHeaderFixedOnScroll,
};
case 'setIsHeaderActivelyFixed':
return {
...state,
isHeaderActivelyFixed: action.isHeaderActivelyFixed,
};
case 'setProductTableActiveWidth':
return {
...state,
productTableActiveWidth: action.productTableActiveWidth,
};
default:
throw new Error(
`Unexpected or missing action type. Action type provided was: ${action.type}`
);
}
};
const ProductTableContext = React.createContext({});
const ProductTableStore = () => {
return React.useContext(ProductTableContext);
};
const ProductTableProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<ProductTableContext.Provider value={[state, dispatch]}>
{children}
</ProductTableContext.Provider>
);
};
export { ProductTableStore, ProductTableProvider };
const ProductTable = ({ heading, ariaLabel, children }) => {
const [globalState, dispatch] = ProductTableStore();
const ProductTableRef = React.useRef(null);
const setIsHeaderActivelyFixed = (isHeaderActivelyFixed) => {
dispatch({
type: 'setIsHeaderActivelyFixed',
isHeaderActivelyFixed,
});
};
const setProductTableActiveWidth = (productTableActiveWidth) => {
dispatch({
type: 'setProductTableActiveWidth',
productTableActiveWidth: `${productTableActiveWidth}px`,
});
};
const useShouldHeaderBeFixed = (ref) => {
if (!globalState.isHeaderFixedOnScroll) return;
// keep mutable refs of values pertinent to the fixed header for the lifetime of the component
const fixedState = React.useRef(null);
const fixedWidth = React.useRef(null);
const [shouldHeaderBeFixed, setShouldHeaderBeFixed] = React.useState(false);
const calculateTablePosition = () => {
if (!fixedState.current && isActivelyViewed(ref.current)) {
setShouldHeaderBeFixed(true);
fixedState.current = true;
} else if (!!fixedState.current && !isActivelyViewed(ref.current)) {
setShouldHeaderBeFixed(false);
fixedState.current = false;
}
};
const calculateTableWidth = () => {
if (fixedWidth.current !== ProductTableRef.current.offsetWidth) {
setProductTableActiveWidth(ProductTableRef.current.offsetWidth);
fixedWidth.current = ProductTableRef.current.offsetWidth;
}
};
const calculateTablePositionAndWidth = () => {
calculateTablePosition();
calculateTableWidth();
};
React.useEffect(() => {
calculateTablePositionAndWidth();
}, []);
React.useEffect(() => {
window.addEventListener('scroll', calculateTablePosition);
window.addEventListener('resize', calculateTablePositionAndWidth);
return () => {
window.removeEventListener('scroll', calculateTablePosition);
window.removeEventListener('resize', calculateTablePositionAndWidth);
};
}, [isActivelyViewed(ref.current)]);
return shouldHeaderBeFixed;
};
// initiallize our custom hook
const shouldHeaderBeFixed = useShouldHeaderBeFixed(ProductTableRef);
// listen only to our custom hook to set global state for the fixed header
React.useEffect(() => {
setIsHeaderActivelyFixed(shouldHeaderBeFixed);
}, [shouldHeaderBeFixed, globalState.isHeaderFixedOnScroll]);
...