Search code examples
javascriptreactjsreact-hooksuse-reducer

reducer function is being called twice


I am practicing React hooks and encountered an issue with useReducer and dispatch functions. The reducer function that I created is being called twice when I'm clicking on either buttons on the SECOND time onwards. My console log output is printed once when clicking either buttons for the first time, and after that on each button push it's printed twice.

Here are my files:

Utils.js

import React, {createContext, useReducer, useContext} from 'react';

const initialState = {

    text: 0
}

const StoreContext = createContext(initialState);

const reducer = (state, action) => {
    console.log('hello');

    switch(action.type) {
        case 'add-counter': return {
            ...state,
            text: state.text + 1
        }
        default:
            throw new Error();
    }
}

export const StoreProvider = ({children}) => {
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <StoreContext.Provider value={{state, dispatch}}>
            {children}
        </StoreContext.Provider>
    )
}

export const useStore = (store) => {
    const {state, dispatch} = useContext(StoreContext);
    return {state, dispatch};
}

UserList.js

import React, {useCallback} from 'react';
import { Row, Col, Button } from 'antd';
import TextDisplayComponent from './TextDisplay';
import {useStore} from '../util';

function ListComponent() {
    const {dispatch} = useStore();
    const increment = useCallback(() => dispatch({type: 'add-counter'}), [dispatch])

    return (
        <Row>
            <Col span={12} style={{textAlign: 'center'}}>
                <Button type="primary" onClick={increment}>Press Me</Button>
            </Col>
            <Col span={12} style={{textAlign: 'center'}}>
                <TextDisplayComponent />
            </Col>
        </Row>
    )
}

export default ListComponent;

TextDisplay.js

import React, {useCallback} from 'react';
import {Button} from 'antd'
import {useStore} from '../util'

function TextDisplayComponent() {
    const {state, dispatch} = useStore();
    const increment = useCallback(() => dispatch({type: 'add-counter'}), [dispatch])

    return (
        <div>
            {state.text}
            <Button onClick={increment}>Add</Button>
        </div>
    )
}

export default TextDisplayComponent

App.js

import React from 'react';
import UserListComponent from './components/UserList';
import 'antd/dist/antd.css';
import {StoreProvider} from './util';

function App() {

  React.createContext()
  return (
    <StoreProvider>
      <div className="App">
        <UserListComponent />
      </div>
    </StoreProvider>
  );
}

export default App;

Can anyone please help? Thanks.

Complete test project can be found at https://github.com/Saro-DA/my-app.git


Solution

  • That's intentional. You're wrapping your app in <React.StrictMode>, which will cause that to happen in development mode.

    Please check this:

    Another thing that React Strict Mode does is run certain callbacks/methods twice (in DEV mode ONLY). You read that right! The following callbacks/methods will be run twice in Strict Mode (in DEV mode ONLY)