cogsbox-state 0.5.361 → 0.5.364

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-state",
3
- "version": "0.5.361",
3
+ "version": "0.5.364",
4
4
  "description": "React state management library with form controls and server sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/CogsState.tsx CHANGED
@@ -1817,6 +1817,7 @@ function createProxyHandler<T>(
1817
1817
  const isLockedToBottomRef = useRef(stickToBottom);
1818
1818
  const isAutoScrolling = useRef(false);
1819
1819
  const prevTotalCountRef = useRef(0);
1820
+ const prevDepsRef = useRef(dependencies);
1820
1821
 
1821
1822
  const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1822
1823
 
@@ -1870,10 +1871,23 @@ function createProxyHandler<T>(
1870
1871
  });
1871
1872
  }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1872
1873
 
1873
- // --- PHASE 1: Detect auto-scroll need and SET THE RANGE ---
1874
+ // --- PHASE 1: Detect change & SET THE RANGE ---
1875
+ // This effect's ONLY job is to decide if we need to auto-scroll and then set the range to the end.
1874
1876
  useLayoutEffect(() => {
1875
1877
  const hasNewItems = totalCount > prevTotalCountRef.current;
1876
- if (isLockedToBottomRef.current && hasNewItems) {
1878
+ const depsChanged = !isDeepEqual(
1879
+ dependencies,
1880
+ prevDepsRef.current
1881
+ );
1882
+
1883
+ if (depsChanged) {
1884
+ isLockedToBottomRef.current = stickToBottom;
1885
+ }
1886
+
1887
+ if (
1888
+ isLockedToBottomRef.current &&
1889
+ (hasNewItems || depsChanged)
1890
+ ) {
1877
1891
  console.log(
1878
1892
  "PHASE 1: Auto-scroll needed. Setting range to render the last item."
1879
1893
  );
@@ -1882,25 +1896,29 @@ function createProxyHandler<T>(
1882
1896
  endIndex: totalCount,
1883
1897
  });
1884
1898
  }
1899
+
1885
1900
  prevTotalCountRef.current = totalCount;
1886
- }, [totalCount]);
1901
+ prevDepsRef.current = dependencies;
1902
+ }, [totalCount, ...dependencies]);
1887
1903
 
1888
- // --- PHASE 2: Wait for measurement and SCROLL ---
1904
+ // --- PHASE 2: Wait for measurement & SCROLL ---
1905
+ // This effect's ONLY job is to run YOUR loop after Phase 1 is complete.
1889
1906
  useLayoutEffect(() => {
1890
1907
  const container = containerRef.current;
1891
- const isRangeAtEnd =
1908
+ const isRangeSetToEnd =
1892
1909
  range.endIndex === totalCount && totalCount > 0;
1893
1910
 
1911
+ // We only start the loop if the range is correctly set to the end and we are locked.
1894
1912
  if (
1895
1913
  !container ||
1896
1914
  !isLockedToBottomRef.current ||
1897
- !isRangeAtEnd
1915
+ !isRangeSetToEnd
1898
1916
  ) {
1899
1917
  return;
1900
1918
  }
1901
1919
 
1902
1920
  console.log(
1903
- "PHASE 2: Range is at the end. Starting the measurement loop."
1921
+ "PHASE 2: Range is set to the end. Starting the measurement loop."
1904
1922
  );
1905
1923
  let loopCount = 0;
1906
1924
  const intervalId = setInterval(() => {
@@ -1927,10 +1945,11 @@ function createProxyHandler<T>(
1927
1945
  top: container.scrollHeight,
1928
1946
  behavior: "smooth",
1929
1947
  });
1948
+ // Give the animation time to finish before unsetting the flag
1930
1949
  setTimeout(() => {
1931
1950
  isAutoScrolling.current = false;
1932
1951
  }, 1000);
1933
- } else if (loopCount > 20) {
1952
+ } else if (loopCount > 30) {
1934
1953
  console.error(
1935
1954
  "LOOP TIMEOUT: Last item was never measured. Stopping loop."
1936
1955
  );
@@ -1941,18 +1960,13 @@ function createProxyHandler<T>(
1941
1960
  }, 100);
1942
1961
 
1943
1962
  return () => clearInterval(intervalId);
1944
- }, [range.endIndex, totalCount, positions]);
1963
+ }, [range]); // This effect is triggered by the `setRange` call in Phase 1.
1945
1964
 
1946
- // --- PHASE 3: Handle User Interaction and Resets ---
1965
+ // --- PHASE 3: Handle User Scrolling ---
1947
1966
  useEffect(() => {
1948
1967
  const container = containerRef.current;
1949
1968
  if (!container) return;
1950
1969
 
1951
- console.log(
1952
- "DEPENDENCY CHANGE: Resetting scroll lock and initial view."
1953
- );
1954
- isLockedToBottomRef.current = stickToBottom;
1955
-
1956
1970
  const updateVirtualRange = () => {
1957
1971
  const { scrollTop, clientHeight } = container;
1958
1972
  let low = 0,
@@ -1993,11 +2007,11 @@ function createProxyHandler<T>(
1993
2007
  container.addEventListener("scroll", handleUserScroll, {
1994
2008
  passive: true,
1995
2009
  });
1996
- updateVirtualRange();
2010
+ updateVirtualRange(); // Always run to set the initial view
1997
2011
 
1998
2012
  return () =>
1999
2013
  container.removeEventListener("scroll", handleUserScroll);
2000
- }, [...dependencies]);
2014
+ }, [totalCount, positions, ...dependencies]);
2001
2015
 
2002
2016
  const scrollToBottom = useCallback(
2003
2017
  (behavior: ScrollBehavior = "smooth") => {