diff --git a/src/App.test.tsx b/src/App.test.tsx
index 2a68616..585ff6e 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -1,9 +1,9 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import App from './App';
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import Apps from "./App";
-test('renders learn react link', () => {
- render();
+test("renders learn react link", () => {
+ render();
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
diff --git a/src/App.tsx b/src/App.tsx
index 5349b8e..f34941f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,10 +1,10 @@
-import logo from './logo.svg';
-import './App.css';
+import "./App.css";
+import ReactUseRefHooks from "./hooks/useref/UseRefHook";
function App() {
return (
-
-
+
+
);
}
diff --git a/src/assets/success.png b/src/assets/success.png
new file mode 100644
index 0000000..3e10f25
Binary files /dev/null and b/src/assets/success.png differ
diff --git a/src/hooks/useref/UseRefHook.tsx b/src/hooks/useref/UseRefHook.tsx
new file mode 100644
index 0000000..f9a9ceb
--- /dev/null
+++ b/src/hooks/useref/UseRefHook.tsx
@@ -0,0 +1,49 @@
+import InfiteScrollView from "./component/InfiniteScrollView";
+import InfiteScrollViewCorrected from "./component/InfiniteScrollViewCorrected";
+import ListComponent from "./component/ListComponent";
+
+export interface ReactUseStateHooksProps {}
+
+export default function ReactUseRefHooks(props: ReactUseStateHooksProps) {
+ return (
+
+
+
+
+
+ );
+}
+
+function Row1() {
+ return (
+ <>
+
+ 1. List scroll component with useRef hook
+
+
+ >
+ );
+}
+
+function Row2() {
+ return (
+ <>
+
+ 2. Infinite List implementation using the useRef hook.
+
+
+ >
+ );
+}
+
+function Row3() {
+ return (
+ <>
+
+ 3. Infinite list implementation using the useRef and UseReducer to
+ correct the loading.
+
+
+ >
+ );
+}
diff --git a/src/hooks/useref/component/InfiniteScrollView.tsx b/src/hooks/useref/component/InfiniteScrollView.tsx
new file mode 100644
index 0000000..05b4a15
--- /dev/null
+++ b/src/hooks/useref/component/InfiniteScrollView.tsx
@@ -0,0 +1,65 @@
+import { Button, CircularProgress, TextField } from "@mui/material";
+import { dataList, DataType } from "./data/ListData";
+import { Ref, useCallback, useEffect, useRef, useState } from "react";
+
+export interface InfiteScrollViewProps {}
+
+type RefType = HTMLDivElement | null;
+
+// Infinite scroll without useReducer it causes issue with loading spinner visibility.
+export default function InfiteScrollViewCorrected(
+ props: InfiteScrollViewProps
+) {
+ const [dataListCurrent, setDataListCurrent] = useState
([]);
+ const [loading, setLoading] = useState(false);
+ const [hasMore, setHasMore] = useState(true);
+
+ const lastIndexRef = useRef(0);
+ const lastListItemRef = useRef(null);
+
+ // Data fetch mock code
+ useEffect(() => {
+ const observer = new IntersectionObserver((enteries) => {
+ if (enteries[0].isIntersecting && hasMore) {
+ setLoading(true);
+ //Code to mock the network request.
+ setTimeout(() => {
+ setDataListCurrent([...dataList.slice(0, lastIndexRef.current + 15)]);
+ lastIndexRef.current = lastIndexRef.current + 15;
+ if (lastIndexRef.current >= dataList.length - 1) {
+ setHasMore(false);
+ }
+ setLoading(false);
+ }, 2000);
+ }
+ });
+ if (lastListItemRef.current) {
+ observer.observe(lastListItemRef.current);
+ }
+
+ return () => {
+ if (lastListItemRef.current) observer.unobserve(lastListItemRef.current);
+ };
+ }, [lastListItemRef]);
+
+ return (
+
+
+ {dataListCurrent.map((data: DataType) => (
+
+ {data.name}
+
+ ))}
+ {loading && (
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/src/hooks/useref/component/InfiniteScrollViewCorrected.tsx b/src/hooks/useref/component/InfiniteScrollViewCorrected.tsx
new file mode 100644
index 0000000..839cf07
--- /dev/null
+++ b/src/hooks/useref/component/InfiniteScrollViewCorrected.tsx
@@ -0,0 +1,103 @@
+import { CircularProgress, Icon, SvgIcon, TextField } from "@mui/material";
+import { dataList, DataType } from "./data/ListData";
+import { useEffect, useReducer, useRef, useState } from "react";
+import Success from "../../../assets/success.png";
+export interface InfiteScrollViewProps {}
+
+type RefType = HTMLDivElement | null;
+
+enum Type {
+ LOADING = "loading",
+ DATA = "data",
+}
+
+/**
+ * Type definition of the action type of the action for component.
+ */
+interface ActionType {
+ type: Type;
+ lastIndex: number;
+}
+
+interface State {
+ data: DataType[];
+ loading: boolean;
+}
+
+/**
+ * Reducer function to manage the states and actions.
+ *
+ * @param state Total number of the states to manage.
+ * @param action Action related to the state we have to manage.
+ * @returns
+ */
+function reducerFunction(state: State, action: ActionType) {
+ console.log(action.type);
+ switch (action.type) {
+ case Type.LOADING:
+ console.log(state.loading);
+ return { data: state.data, loading: !state.loading };
+ case Type.DATA:
+ return {
+ data: [...dataList.slice(0, action.lastIndex + 15)],
+ loading: state.loading,
+ };
+ }
+}
+
+const initialState: State = { data: [], loading: false };
+
+// Infinite scroll without useReducer it causes issue with loading spinner visibility.
+export default function InfiteScrollView(props: InfiteScrollViewProps) {
+ const [state, dispatch] = useReducer(reducerFunction, initialState);
+
+ const lastIndexRef = useRef(0);
+ const lastListItemRef = useRef(null);
+
+ // Data fetch mock code
+ useEffect(() => {
+ const observer = new IntersectionObserver((enteries) => {
+ if (enteries[0].isIntersecting) {
+ dispatch({ type: Type.LOADING, lastIndex: lastIndexRef.current });
+ //Code to mock the network request.
+ setTimeout(() => {
+ dispatch({ type: Type.DATA, lastIndex: lastIndexRef.current });
+ dispatch({ type: Type.LOADING, lastIndex: lastIndexRef.current });
+ lastIndexRef.current = lastIndexRef.current + 15;
+ if (lastIndexRef.current >= dataList.length - 1) {
+ }
+ }, 3000);
+ }
+ });
+ if (lastListItemRef.current) {
+ observer.observe(lastListItemRef.current);
+ }
+
+ return () => {
+ if (lastListItemRef.current) observer.unobserve(lastListItemRef.current);
+ };
+ }, [lastListItemRef]);
+
+ return (
+
+
+ {state.data.map((data: DataType) => (
+
+ {data.name}
+
+ ))}
+
+
+
+ {state.loading ? (
+
+ ) : (
+

+ )}
+
+
+ );
+}
diff --git a/src/hooks/useref/component/ListComponent.tsx b/src/hooks/useref/component/ListComponent.tsx
new file mode 100644
index 0000000..6e094d2
--- /dev/null
+++ b/src/hooks/useref/component/ListComponent.tsx
@@ -0,0 +1,73 @@
+import { Button, TextField } from "@mui/material";
+import { dataList, DataType } from "./data/ListData";
+import { useRef, useState } from "react";
+import { text } from "stream/consumers";
+
+export interface IListComponentProps {}
+
+type RefType = HTMLUListElement | null;
+
+export default function ListComponent(props: IListComponentProps) {
+ const [index, setIndex] = useState(0);
+
+ function scrollToIndex(index: number) {
+ const listNode: RefType = listRef.current;
+ const childNode = listNode!.querySelectorAll("ul > div")[index];
+ childNode.scrollIntoView({
+ behavior: "smooth",
+ block: "end",
+ inline: "center",
+ });
+ }
+
+ const listRef = useRef(null);
+
+ const handleFirstButtonClick = () => {
+ scrollToIndex(0);
+ };
+
+ const handleScrollToIndexButtonClick = () => {
+ scrollToIndex(index);
+ };
+
+ const handleSecondButtonClick = () => {
+ scrollToIndex(dataList.length - 1);
+ };
+
+ return (
+
+
+
+
+ {
+ setIndex(e.target.value);
+ }}
+ label="Index"
+ />
+
+
+
+
+ {dataList.map((data: DataType) => (
+
+ {data.name}
+
+ ))}
+
+
+ );
+}
diff --git a/src/hooks/useref/component/data/ListData.tsx b/src/hooks/useref/component/data/ListData.tsx
new file mode 100644
index 0000000..62d0409
--- /dev/null
+++ b/src/hooks/useref/component/data/ListData.tsx
@@ -0,0 +1,73 @@
+/**
+ * Type definition for the data string.
+ */
+export interface DataType {
+ id: number;
+ name: string;
+}
+
+/**
+ * Data list to show the infinite list thing.
+ */
+export const dataList: DataType[] = [
+ { id: 1, name: "Services1" },
+ { id: 2, name: "Engineering" },
+ { id: 3, name: "Services2" },
+ { id: 4, name: "Training" },
+ { id: 5, name: "Support" },
+ { id: 6, name: "Research and Development" },
+ { id: 7, name: "Training" },
+ { id: 8, name: "Human Resources" },
+ { id: 9, name: "Services2" },
+ { id: 10, name: "Legal1" },
+ { id: 11, name: "Sales" },
+ { id: 12, name: "Legal2" },
+ { id: 13, name: "Accounting" },
+ { id: 14, name: "Business Development" },
+ { id: 15, name: "Accounting" },
+ { id: 16, name: "Services3" },
+ { id: 17, name: "Training" },
+ { id: 18, name: "Research and Development" },
+ { id: 19, name: "Human Resources" },
+ { id: 20, name: "Legal3" },
+ { id: 21, name: "Research and Development" },
+ { id: 22, name: "Human Resources" },
+ { id: 23, name: "Services4" },
+ { id: 24, name: "Research and Development" },
+ { id: 25, name: "Research and Development" },
+ { id: 26, name: "Accounting" },
+ { id: 27, name: "Product Management" },
+ { id: 28, name: "Human Resources" },
+ { id: 29, name: "Legal4" },
+ { id: 30, name: "Legal5" },
+ { id: 31, name: "Services5" },
+ { id: 32, name: "Engineering1" },
+ { id: 33, name: "Services6" },
+ { id: 34, name: "Training" },
+ { id: 35, name: "Support" },
+ { id: 36, name: "Research and Development" },
+ { id: 37, name: "Training" },
+ { id: 38, name: "Human Resources" },
+ { id: 39, name: "Services7" },
+ { id: 40, name: "Legal6" },
+ { id: 41, name: "Sales" },
+ { id: 42, name: "Legal7" },
+ { id: 43, name: "Accounting" },
+ { id: 44, name: "Business Development" },
+ { id: 45, name: "Accounting" },
+ { id: 46, name: "Services7" },
+ { id: 47, name: "Training" },
+ { id: 48, name: "Research and Development" },
+ { id: 49, name: "Human Resources" },
+ { id: 50, name: "Legal8" },
+ { id: 51, name: "Research and Development" },
+ { id: 52, name: "Human Resources" },
+ { id: 53, name: "Services8" },
+ { id: 54, name: "Research and Development" },
+ { id: 55, name: "Research and Development" },
+ { id: 56, name: "Accounting" },
+ { id: 57, name: "Product Management" },
+ { id: 58, name: "Human Resources" },
+ { id: 59, name: "Legal9" },
+ { id: 60, name: "Legal10" },
+];
diff --git a/src/index.tsx b/src/index.tsx
index 032464f..bfb6316 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,15 +1,15 @@
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import './index.css';
-import App from './App';
-import reportWebVitals from './reportWebVitals';
+import React from "react";
+import ReactDOM from "react-dom/client";
+import "./index.css";
+import Apps from "./App";
+import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(
- document.getElementById('root') as HTMLElement
+ document.getElementById("root") as HTMLElement
);
root.render(
-
+
);