Reactjs React钩子-上下文提供程序未立即反映已调度状态

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。否则,每当表处于视图中

我的目标: 我正在创建一个向用户显示数据的表。基于存储在全局状态中的某个值(存储在上下文API提供给其他组件的reducer函数中),我将在scroll上将该标题固定到页面顶部,但仅当该表处于视图中时。为此,我必须注册一个on Scroll和on Resize事件侦听器,以便在用户滚动或调整屏幕大小时重新计算表的位置。仅当表在视图中且状态尚未设置为
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]);
...