<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: LawrenceKarasek</title>
    <description>The latest articles on DEV Community by LawrenceKarasek (@lawrencekarasek).</description>
    <link>https://dev.to/lawrencekarasek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1039952%2Fc7089eee-6f97-4132-88cc-99f39c797d5e.jpeg</url>
      <title>DEV Community: LawrenceKarasek</title>
      <link>https://dev.to/lawrencekarasek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lawrencekarasek"/>
    <language>en</language>
    <item>
      <title>Using advanced ES6 functions and unit testing in a UI app</title>
      <dc:creator>LawrenceKarasek</dc:creator>
      <pubDate>Sun, 19 Mar 2023 14:26:52 +0000</pubDate>
      <link>https://dev.to/lawrencekarasek/using-advanced-es6-functions-and-unit-testing-in-a-ui-app-1kbc</link>
      <guid>https://dev.to/lawrencekarasek/using-advanced-es6-functions-and-unit-testing-in-a-ui-app-1kbc</guid>
      <description>&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;In this Tic Tac Toe app, I demonstrate the use of javascript ES6 functions including reducer, filter, map, splice and findIndex. This also includes unit testing with multiple user events.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project setup
&lt;/h1&gt;

&lt;p&gt;The project is bootstrapped with Create React App and also includes eslint and prettier to improve clean code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project structure
&lt;/h1&gt;

&lt;p&gt;Data.js:  provides aysnchronous access to json data for the board&lt;/p&gt;

&lt;p&gt;Components: Board and Cell components with unit tests.&lt;/p&gt;

&lt;p&gt;The complete code can be found here: &lt;a href="https://github.com/LawrenceKarasek/TicTacToe"&gt;https://github.com/LawrenceKarasek/TicTacToe&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Running the project
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
npm install npm start

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, to check for any coding issues and correct formatting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
npm run eslint 
npm run format

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Loading Data
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Board.js
&lt;/h2&gt;

&lt;p&gt;Data is loaded asynchronously using a Promise. The useEffect hook includes fetchData in its dependency array and calls the fetchData method on initial loading. To prevent unneccessary reloads, fetchData is contained in a useCallback. This ensures the fetchData function is memoized (cached). Otherwise, each time useEffect is called, a new version of the function would be created and useEffect would call fetchData again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const [cells, setCells] = useState();
const fetchData = useCallback(async () =&amp;gt; {
getData()
.then(result =&amp;gt; setCells(result))
.catch(err =&amp;gt; console.error(err));
}, []);

useEffect(() =&amp;gt; {
fetchData();
}, [fetchData]);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fetchData methods calls the getData function in Data.js asynchronously. Since Promises are being used with "thenable", it allows the results to be assigned to the state in setVideos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data.js
&lt;/h2&gt;

&lt;p&gt;The getData method in Data.js uses a Promise to asynchronously load json data using 'resolve'. If an error occurs, the Promise returns 'reject' with the error message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const getData = () =&amp;gt; {
return new Promise((resolve, reject) =&amp;gt; {
try{
if (data) {
resolve(data);
} else {
reject('No data is available.');
}
}
catch(e){
reject('An error occurred fetching data:' + e);
};
});
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Rendering
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Board.js
&lt;/h2&gt;

&lt;p&gt;After the cell data is loaded into state, cells are written one row at a time. The WriteCellRow method filters the cells for each row then returns an array of cells:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  const writeCellRow = (row) =&amp;gt; {
    return cells
      .filter((f) =&amp;gt; f.row === row)
      .map((item) =&amp;gt; (
        &amp;lt;Cell
          cellData={item}
          key={item.row + "_" + item.column}
          updateCell={updateCell}
        /&amp;gt;
      ));
  };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UI includes a wonRef which uses the useRef webHook to maintain the status of the board between state re-renders. This was used because if this is maintained in state along with the cell state, the behavior in re-rendering the board is unpredictable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  return (
    &amp;lt;Fragment&amp;gt;
      &amp;lt;h1 className="header"&amp;gt; Board &amp;lt;/h1&amp;gt;
      &amp;lt;h2 className="won"&amp;gt; {wonRef.current === true &amp;amp;&amp;amp; "YOU WON!!"} &amp;lt;/h2&amp;gt;
      {cells &amp;amp;&amp;amp; (
        &amp;lt;Fragment&amp;gt;
          &amp;lt;div className="board"&amp;gt;
            &amp;lt;div className="cellRow"&amp;gt;{writeCellRow(1)}&amp;lt;/div&amp;gt;
            &amp;lt;div className="cellRow"&amp;gt;{writeCellRow(2)}&amp;lt;/div&amp;gt;
            &amp;lt;div className="cellRow"&amp;gt;{writeCellRow(3)}&amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; clearBoard()}&amp;gt;
              Clear Board
            &amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/Fragment&amp;gt;
      )}
    &amp;lt;/Fragment&amp;gt;
  );
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cell.js
&lt;/h2&gt;

&lt;p&gt;Each cell receives its State from the Board with a callback for updating the state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const Cell = ({ cellData, updateCell }) =&amp;gt; {
  return (
    &amp;lt;button className="cell" role="cell" onClick={() =&amp;gt; updateCell(cellData)}&amp;gt;
      &amp;lt;span className="cellText"&amp;gt;{cellData.state}&amp;lt;/span&amp;gt;
    &amp;lt;/button&amp;gt;
  );
};

Cell.propTypes = {
  cellData: PropTypes.object,
  updateCell: PropTypes.func,
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Updating the Cells
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Board.js
&lt;/h2&gt;

&lt;p&gt;Cells in the Board are updated from "X" to "O" then back to null as follows. Note it is necessary to copy the initial array of cells before the state is updated using setCells to create a new reference in memory. The single cell is updated by using the array findIndex and then splice methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  const updateCell = (cell) =&amp;gt; {
    let updatedState = "";
    switch (cell.state) {
      case "X":
        updatedState = "O";
        break;
      case "O":
        updatedState = null;
        break;
      default:
        updatedState = "X";
        break;
    }
    cell.state = updatedState;

    let cellFilteredIndex = cells.findIndex(
      (c) =&amp;gt; c.row === cell.row &amp;amp;&amp;amp; c.column === cell.column
    );

    let cellsCopy = [...cells];
    cellsCopy.splice(cellFilteredIndex, 1, cell);
    setCells(cellsCopy);
    checkScore();
  };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Checking the Score
&lt;/h2&gt;

&lt;p&gt;In order to check the score, each row and column plus the tewo diagonals must be checkd for both "X" and "O". This is done sequentiually using the checkRowsColumnsWon function, which takes the type of check ("row", "column" or "diagonal") and the row or coilumn number, if applicable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  const checkScore = () =&amp;gt; {
    let isWin = false;

    for (let x = 1; x &amp;lt;= 3; x++) {
      isWin = checkRowsColumnsWon("row", x, "X");
      if (isWin) {
        break;
      }
      isWin = checkRowsColumnsWon("row", x, "O");
      if (isWin) {
        break;
      }
      isWin = checkRowsColumnsWon("column", x, "X");
      if (isWin) {
        break;
      }
      isWin = checkRowsColumnsWon("column", x, "O");
      if (isWin) {
        break;
      }
    }
    if (!isWin) {
      isWin = checkRowsColumnsWon("diagonal", null, "X");
      if (!isWin) {
        isWin = checkRowsColumnsWon("diagonal", null, "O");
      }
    }
    if (isWin) {
      wonRef.current = true;
    }
  };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checkRowsColumnsWon function uses the reduce function to return a boolean for each type of check. For eeach item in the array, reduce updates the accummulator parameter, in this case, "isWon". If the row or column number and state match, the rowCount is incremented, and isWon is set to true when the rowCount = 3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  const checkRowsColumnsWon = (type, number, state) =&amp;gt; {
    let wonRowsColumns = false;
    let rowCount = 0;

    switch (type) {
      case "row":
        wonRowsColumns = cells.reduce((isWon, f) =&amp;gt; {
          rowCount =
            f.row === number &amp;amp;&amp;amp; f.state === state ? rowCount + 1 : rowCount;
          return (isWon || rowCount === 3);
        }, false);
        break;
      case "column":
        wonRowsColumns = cells.reduce((isWon, f) =&amp;gt; {
          rowCount =
            f.column === number &amp;amp;&amp;amp; f.state === state ? rowCount + 1 : rowCount;
          return (isWon || rowCount === 3);
        }, false);
        break;
      case "diagonal":
        wonRowsColumns = cells.reduce((isWon, f) =&amp;gt; {
          rowCount =
            (f.row === 1 &amp;amp;&amp;amp; f.column === 1 &amp;amp;&amp;amp; f.state === state) ||
            (f.row === 2 &amp;amp;&amp;amp; f.column === 2 &amp;amp;&amp;amp; f.state === state) ||
            (f.row === 3 &amp;amp;&amp;amp; f.column === 3 &amp;amp;&amp;amp; f.state === state)
              ? rowCount + 1
              : rowCount;
          return (isWon || rowCount === 3);
        }, false);
        if (!wonRowsColumns) {
          wonRowsColumns = cells.reduce((isWon, f) =&amp;gt; {
            rowCount =
              (f.row === 3 &amp;amp;&amp;amp; f.column === 3 &amp;amp;&amp;amp; f.state === state) ||
              (f.row === 2 &amp;amp;&amp;amp; f.column === 2 &amp;amp;&amp;amp; f.state === state) ||
              (f.row === 3 &amp;amp;&amp;amp; f.column === 1 &amp;amp;&amp;amp; f.state === state)
                ? rowCount + 1
                : rowCount;
            return (isWon || rowCount === 3);
          }, false);
        }
        break;
      default:
        wonRowsColumns = false;
        break;
    }
    return wonRowsColumns;
  };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Unit tests
&lt;/h1&gt;

&lt;p&gt;The Board and Cell components are unit tested to verify they are properly loaded and the updating and scoring functionality is working correctly. The actual user interactions are checked using the React testing library.&lt;/p&gt;

&lt;h1&gt;
  
  
  Board.test.js
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
describe("Board is rendered correctly", () =&amp;gt; {
  it("renders the Board and displays correct number of cells", async () =&amp;gt; {
    render(&amp;lt;Board /&amp;gt;);
    const clearButton = await screen.findByText("Clear Board");
    expect(clearButton).toBeDefined();
    const cells = await screen.findAllByRole("cell");
    expect(cells).toHaveLength(9);
  });
});

describe("selecting 3 X's or O's in a row results in winning", () =&amp;gt; {
  let cellList = null;
  let cellButton = null;
  let clearButton = null;

  it("3 X's in a row result in winning", async () =&amp;gt; {
    render(&amp;lt;Board /&amp;gt;);

    await waitFor(async () =&amp;gt; {
      cellList = await screen.findAllByRole("cell");
      cellButton = cellList[0];
    });

    fireEvent.click(cellButton);

    await waitFor(async () =&amp;gt; {
      const updatedFirstButton = await screen.findByText("X");
      expect(updatedFirstButton).not.toBeNull();
    });

    cellButton = cellList[1];
    fireEvent.click(cellButton);

    await waitFor(async () =&amp;gt; {
      const updatedButtons = await screen.findAllByText("X");
      expect(updatedButtons).toHaveLength(2);
    });

    cellButton = cellList[2];
    fireEvent.click(cellButton);

    await waitFor(async () =&amp;gt; {
      const updatedButtons = await screen.findAllByText("X");
      expect(updatedButtons).toHaveLength(3);
    });

    await waitFor(async () =&amp;gt; {
      const wonButton = await screen.findByText("YOU WON!!");
      expect(wonButton).toBeDefined();
    });

    await waitFor(async () =&amp;gt; {
      clearButton = await screen.findByText("Clear Board");
    });

    fireEvent.click(clearButton);

    await waitFor(async () =&amp;gt; {
      cellList = await screen.findAllByRole("cell");
      cellButton = cellList[0];
      expect(cellButton).toHaveValue("");
    });
  });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Cell.test.js

The cell is mocked to verify it is rendering correctly and the updateCell function is called. Mockingthe data for a component is less realistic then actual user interaction but necessary in many cases since a component depends on live data. 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;const cellData = {&lt;br&gt;
  row: 1,&lt;br&gt;
  column: 1,&lt;br&gt;
  state: "X",&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;describe("Cell is rendered correctly", () =&amp;gt; {&lt;br&gt;
  const updateCell = jest.fn();&lt;br&gt;
  let cellButton = null;&lt;/p&gt;

&lt;p&gt;it("renders the Cell correctly", async () =&amp;gt; {&lt;br&gt;
    render(&lt;br&gt;
      
        cellData={cellData}&lt;br&gt;
        key={cellData.row + "_" + cellData.column}&lt;br&gt;
        updateCell={updateCell}&lt;br&gt;
      /&amp;gt;&lt;br&gt;
    );&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await waitFor(async () =&amp;gt; {
  cellButton = await screen.findByText("X");
  expect(cellButton).toBeDefined();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});&lt;/p&gt;

&lt;p&gt;it("clicking Cell calls update ", async () =&amp;gt; {&lt;br&gt;
    let cellButton = null;&lt;br&gt;
    render(&lt;br&gt;
      
        cellData={cellData}&lt;br&gt;
        key={cellData.row + "_" + cellData.column}&lt;br&gt;
        updateCell={updateCell}&lt;br&gt;
      /&amp;gt;&lt;br&gt;
    );&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await waitFor(async () =&amp;gt; {
  cellButton = await screen.findByText("X");
  expect(cellButton).not.toBeNull();
});

fireEvent.click(cellButton);

await waitFor(async () =&amp;gt; {
  expect(updateCell).toHaveBeenCalledTimes(1);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});&lt;br&gt;
});&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Conclusion

I hope this is helpful for those learning how to use advanced javascript ES6 functions effectively in front end code. I welcome your feedback to improve this article. All the Best!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>reduce</category>
      <category>filter</category>
    </item>
    <item>
      <title>Using React hooks to develop a Video Player</title>
      <dc:creator>LawrenceKarasek</dc:creator>
      <pubDate>Mon, 06 Mar 2023 23:23:33 +0000</pubDate>
      <link>https://dev.to/lawrencekarasek/using-react-hooks-to-develop-a-video-player-5d5p</link>
      <guid>https://dev.to/lawrencekarasek/using-react-hooks-to-develop-a-video-player-5d5p</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The use of hooks in React when managing interactive video can be challenging when it comes to optimizing performance and providing a good user experience. In this article, I demonstrate how a video player with content that is dynamically loading can be managed effectively with useEffect, useRef, useCallback and useState web hooks.&lt;/p&gt;

&lt;p&gt;The project includes a simple component hierarchy, data layer and different unit testing methods to help achieve best practices.&lt;/p&gt;

&lt;p&gt;The complete source code is available &lt;a href="https://github.com/LawrenceKarasek/VideoPlayer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;The project is bootstrapped with &lt;a href="https://github.com/facebook/create-react-app"&gt;Create React App&lt;/a&gt; and also includes &lt;a href="https://eslint.org/"&gt;eslint&lt;/a&gt; and &lt;a href="https://prettier.io/"&gt;prettier&lt;/a&gt; to ensure clean code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;App.js: entry point for the application.&lt;/li&gt;
&lt;li&gt;Data.js: provides aysnchronous access to json data for videos.&lt;/li&gt;
&lt;li&gt;Components/VideoPlayer.js and VedioControls.js. UI for user interaction with the Video player.&lt;/li&gt;
&lt;li&gt;Unit tests: App and Components each have their own unit tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running the project:&lt;br&gt;
&lt;code&gt;npm install npm start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Also to check for any coding issues:&lt;br&gt;
&lt;code&gt;npm run eslint npm run format&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;App.js&lt;/p&gt;

&lt;p&gt;Data is loaded asynchronously in App.js. The useEffect hook includes fetchData in its dependency array and calls the fetchData method on initial loading. To prevent unneccessary reloads, fetchData is contained in a useCallback. This ensures the fetchData function is memoized (cached). Otherwise, each time useEffect is called, a new version of the function would be created and useEffect would call fetchData again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [videos, setVideos] = useState();
const fetchData = useCallback(async () =&amp;gt; {
    getData()
    .then(result =&amp;gt; setVideos(result))
    .catch(err =&amp;gt; console.error(err));
}, []);

useEffect(() =&amp;gt; {
    fetchData();
}, [fetchData]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fetchData methods calls the getData function in Data.js asynchronously. Since Promises are being used with "thenable", it allows the results to be assigned to the state in setVideos. (Note: In a real-world scenario, where there could be multiple components using Videos, state would be stored at the application level using react-redux and the Store (or another application-level mechanism such as useContext) rather than at the component level. This would allow data access across different components.)&lt;/p&gt;

&lt;p&gt;Data.js&lt;/p&gt;

&lt;p&gt;The getData method in Data.js uses a Promise to asynchronously load json data using 'resolve'. If an error occurs, the Promise returns 'reject' with the error message. (Note: In a real-world scenario, data would be retrieved using a remote request from a remote URL using axios or similar plug-in).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getData = () =&amp;gt; {
return new Promise((resolve, reject) =&amp;gt; {
    try{
    if (data) {
    resolve(data);
    } else {
    reject('No data is available.');
    }
    }
    catch(e){
    reject('An error occurred fetching data:' + e);
    };
});
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  VideoPlayer
&lt;/h2&gt;

&lt;p&gt;The VideoPlayer receives Videos from the main app: &lt;br&gt;
&lt;code&gt;VideoPlayer = ({ videos }) =&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Videos are played based on the videoIndex state property. The videoSrc maintains the current video and isPlaying sets the video to start. Video progress and duration are used to set the progress bar properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const [videoIndex, setVideoIndex] = useState(0);
const [videoProgress, setVideoProgress] = useState(0);
const [duration, setDuration] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const { videoSrc, link } = videos[videoIndex];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the videoIndex or videoSrc changes based on the dependencies in useEffect, the video source is reset, isPlaying is re-enabled which causes the useEffect with isPlaying dependency to execute, which then starts playing the video. The time is also started and progress bar updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    useEffect(() =&amp;gt; {
        if (!isPlaying) {
        videoRef.current.pause();
        videoRef.current.src = videoSrc;
        setIsPlaying(true);
        }
    }, [videoIndex, videoSrc]);

        useEffect(() =&amp;gt; {
        if (isPlaying) {
        videoRef.current.play();
        setVideoProgress(videoRef.current.currentTime);
        startTimer();
        } else {
        videoRef.current.pause();
        }
    }, [isPlaying]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current video that is playing can be switched using the VideoControls next and previous buttons as well as the onEnd event. When these events are raised, isPlaying is set to false the videoIndex is updated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const toPrevVideo = () =&amp;gt; {
        setIsPlaying(false);
        if (videoIndex - 1 &amp;lt; 0) {
        setVideoIndex(videos.length - 1);
        } else {
        setVideoIndex(videoIndex - 1);
        }
    };

    const toNextVideo = () =&amp;gt; {
        setIsPlaying(false);
        if (videoIndex &amp;lt; videos.length - 1) {
        setVideoIndex(videoIndex + 1);
        } else {
        setVideoIndex(0);
        }
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HMTLVideoElement is maintained through re-renders by the use of the useRef hook, videoRef. There are built-in events and properties of the Video: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;poster provides a default background before the video is loaded. &lt;/li&gt;
&lt;li&gt;onDurationChangeHandler updates the state for the progress bar &lt;/li&gt;
&lt;li&gt;onEnded manages the next video to play once the current one completes
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        const videoRef = useRef();

        .....

        &amp;lt;video
          ref={videoRef}
          className="video"
          onDurationChange={onDurationChangeHandler}
          onEnded={onEnded}
          poster={'/giphyloading.gif'}
          autoPlay={true}
          muted={true}
        &amp;gt;
        &amp;lt;/video&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data for the progress bar is maintained using the intervalRef:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
const intervalRef = useRef();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The intervalRef function updates the videoProgress each second in the startTimer function. It is called when the video is started. The interval is reset when the video ends.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const onDurationChangeHandler = e =&amp;gt; { const seconds = Math.floor(e.target.duration); setDuration(seconds); };
const onEnded = () =&amp;gt; { clearInterval(intervalRef.current); toNextVideo(); };
const startTimer = () =&amp;gt; { clearInterval(intervalRef.current);

intervalRef.current = setInterval(() =&amp;gt; {
  if (videoRef.current) {
    setVideoProgress(videoRef.current.currentTime);
  }
}, [1000]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  VideoControls
&lt;/h2&gt;

&lt;p&gt;VideoControls receives callback handlers to respond to events from buttons for Play, Pause, Next and Previous. The isPlaying state determines if Play or Pause is shown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const VideoControls = ({ isPlaying, onPlayPauseClick, onPrevClick, onNextClick }) =&amp;gt; (
&amp;lt;div className="video-controls"&amp;gt;
    &amp;lt;button type="button" className="prev" aria-label="Previous" onClick={onPrevClick}&amp;gt;
    &amp;lt;Prev /&amp;gt;
    &amp;lt;/button&amp;gt;
    {isPlaying ? (
    &amp;lt;button
        type="button"
        className="pause"
        data-testid="pause-test-id"
        onClick={() =&amp;gt; onPlayPauseClick(false)}
        aria-label="Pause"
    &amp;gt;
        &amp;lt;Pause /&amp;gt;
    &amp;lt;/button&amp;gt;
    ) : (
    &amp;lt;button type="button" className="play" onClick={() =&amp;gt; onPlayPauseClick(true)} aria-label="Play"&amp;gt;
        &amp;lt;Play /&amp;gt;
    &amp;lt;/button&amp;gt;
    )}
    &amp;lt;button type="button" className="next" aria-label="Next" onClick={onNextClick}&amp;gt;
    &amp;lt;Next /&amp;gt;
    &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unit tests
&lt;/h2&gt;

&lt;p&gt;App.js, VideoPlayer and VideoControls are unit tested to verify they are properly loaded. The objective of unit testing is to reflect user interaction as realistically as possible. To that end, the react testing library is used for App and VideoPlayer to render the document, initiate user events and assert the results are correct. Also, shallow rendering is used with the react test renderer to verify the app loaded is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this is helpful for those learning how to use web hooks effectively. I welcome your feedback to improve this article, All the Best!&lt;/p&gt;

</description>
      <category>react</category>
      <category>useref</category>
      <category>usecallback</category>
      <category>useeffects</category>
    </item>
  </channel>
</rss>
