├── src
├── react-app-env.d.ts
├── index.tsx
├── setupTests.ts
├── App.test.tsx
├── App.tsx
└── use-non-initial-effect-hook.tsx
├── public
├── robots.txt
└── index.html
├── README.md
├── .gitignore
├── tsconfig.json
└── package.json
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # useNonInitialEffectHook
2 | More details about this hook can be found here:
3 | https://www.thearmchaircritic.org/tech-journals/prevent-useeffects-callback-firing-during-initial-render
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
12 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render, wait } from "@testing-library/react";
3 | import App from "./App";
4 |
5 | test("Test hook", () => {
6 | const { getByTestId } = render();
7 | const button = getByTestId("button");
8 | const counter = getByTestId("pressed");
9 | expect(counter).toHaveTextContent("The button has not been pressed!");
10 | button.click();
11 | wait(
12 | () => {
13 | expect(counter).toHaveTextContent("The button has been pressed!");
14 | },
15 | { timeout: 100 }
16 | );
17 | });
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useNonInitialEffect } from "./use-non-initial-effect-hook";
3 |
4 | function App() {
5 | const [press, setPress] = useState(false);
6 | const [pressed, setPressed] = useState(false);
7 |
8 | /**
9 | * Increment the counter every time toggle changes.
10 | */
11 | useNonInitialEffect(() => {
12 | setPressed(true);
13 | }, [press]);
14 |
15 | return (
16 |
17 |
18 | {pressed ? "The button has been pressed!" : "The button has not been pressed!"}
19 |
20 |
28 |
29 | );
30 | }
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/src/use-non-initial-effect-hook.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, EffectCallback, DependencyList, useRef } from "react";
2 |
3 | /**
4 | * This hook gets called only when the dependencies change but not during initial render.
5 | *
6 | * @param {EffectCallback} effect The `useEffect` callback function.
7 | * @param {DependencyList} deps An array of dependencies.
8 | *
9 | * @example
10 | * ```
11 | * useNonInitialEffect(()=>{
12 | * alert("Dependency changed!");
13 | * },[dependency]);
14 | * ```
15 | */
16 | export const useNonInitialEffect = (effect: EffectCallback, deps?: DependencyList) => {
17 | const initialRender = useRef(true);
18 |
19 | useEffect(() => {
20 | let effectReturns: void | (() => void | undefined) = () => {};
21 |
22 | if (initialRender.current) {
23 | initialRender.current = false;
24 | } else {
25 | effectReturns = effect();
26 | }
27 |
28 | if (effectReturns && typeof effectReturns === "function") {
29 | return effectReturns;
30 | }
31 | }, deps);
32 | };
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "use-non-initial-effect-hook",
3 | "version": "0.1.0",
4 | "author":"Thivi",
5 | "private": true,
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.5.0",
9 | "@testing-library/user-event": "^7.2.1",
10 | "@types/jest": "^24.9.1",
11 | "@types/node": "^12.12.47",
12 | "@types/react": "^16.9.41",
13 | "@types/react-dom": "^16.9.8",
14 | "react": "^16.13.1",
15 | "react-dom": "^16.13.1",
16 | "react-scripts": "3.4.1",
17 | "typescript": "^3.7.5"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": "react-app"
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
25 | UseNonInitialEffect Hook
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------