title: UseReducer is called twice when dispatching data from useEffect hook within child component
body: <p>I am making a todo/shopping list in ReactJS. Besides being able to add items manually to the list by input, the user should also be able to add items programmatically.</p>
  <p>I am using <code>createContext()</code> and <code>useReducer</code> for managing the <code>state()</code>.</p>
  <p>When I add items programmatically by providing an array through the props and listen for changes in <code>useEffect</code>, the <code>useEffect</code> and <code>dispatch</code> fires twice despite that I only changed the props once.</p>
  <p>However this is NOT happening when I provide the array of items through props the first time.</p>
  <p>Consequently, after the first time, when <code>dispatch</code> fires twice the list get duplicates (also duplicate keys).</p>
  <p>Is it happening due to some re-rendering process that I am not aware of? Any help is much appreciated as I am really stuck on this one.</p>
  <p>Here is the code:</p>
  <p>Context provider component containing the useEffect that triggers the dispatch method from useReducer when the props change:</p>
  <pre class="lang-js prettyprint-override"><code>import React, { createContext, useEffect, useReducer } from 'react';
  import todosReducer from '../reducers/todos.reducer';
  import { ADD_INGREDIENT_ARRAY } from '../constants/actions';
  
  const defaultItems = [
    { id: '0', task: 'Item1', completed: false },
    { id: '1', task: 'Item2', completed: false },
    { id: '2', task: 'Item3', completed: false }
  ];
  
  export const TodosContext = createContext();
  export const DispatchContext = createContext();
  
  export function TodosProvider(props) {
    const [todos, dispatch] = useReducer(todosReducer, defaultItems)
  
    useEffect(() =&gt; {
      if (props.ingredientArray.length) {
        dispatch({ type: ADD_INGREDIENT_ARRAY, task: props.ingredientArray });
      }
    }, [props.ingredientArray])
  
    return (
      &lt;TodosContext.Provider value={todos}&gt;
        &lt;DispatchContext.Provider value={dispatch}&gt;
          {props.children}
        &lt;/DispatchContext.Provider&gt;
      &lt;/TodosContext.Provider&gt;
    );
  }
  </code></pre>
  <p>My reducer function (<code>ADD_INGREDIENT_ARRAY</code> is the one that gets called from above code snippet) :</p>
  <pre><code>import uuidv4 from &quot;uuid/dist/v4&quot;;
  import { useReducer } from &quot;react&quot;;
  import {
    ADD_TODO,
    REMOVE_TODO,
    TOGGLE_TODO,
    EDIT_TODO,
    ADD_INGREDIENT_ARRAY
  } from '../constants/actions';
  
  const reducer = (state, action) =&gt; {
    switch (action.type) {
      case ADD_TODO:
        return [{ id: uuidv4(), task: action.task, completed: false }, ...state];
      case REMOVE_TODO:
        return state.filter(todo =&gt; todo.id !== action.id);
      case TOGGLE_TODO:
        return state.map(todo =&gt;
          todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
        );
      case EDIT_TODO:
        return state.map(todo =&gt;
          todo.id === action.id ? { ...todo, task: action.task } : todo
        );
      case ADD_INGREDIENT_ARRAY: 
          console.log('THE REDUCER WAS CALLED')
          return [...action.task.map(ingr =&gt; ({ id: uuidv4(), task: ingr.name, completed: false }) ), ...state]
      default:
        return state;
    }
  };
  
  export default reducer;
  </code></pre>
  <p>The list component that renders each item and uses the context from above code snippet:</p>
  <pre class="lang-js prettyprint-override"><code>import React, { useContext, useEffect, useState } from 'react';
  import { TodosContext, DispatchContext } from '../contexts/todos.context';
  import Todo from './Todo';
  
  function TodoList() {
    const todos = useContext(TodosContext);
  
    return (
      &lt;ul style={{ paddingLeft: 10, width: &quot;95%&quot; }}&gt;
        {todos.map(todo =&gt; (
          &lt;Todo key={Math.random()} {...todo} /&gt;
        ))}
      &lt;/ul&gt;
    );
  }
  
  export default TodoList;
  </code></pre>
  <p>And the app component containing the list which is wrapped in the context provider that passes the props:</p>
  <pre class="lang-js prettyprint-override"><code>import React, { useEffect, useReducer } from 'react';
  import { TodosProvider } from '../contexts/todos.context';
  import TodoForm from './TodoForm';
  import TodoList from './TodoList';
  
  function TodoApp({ ingredientArray }) {
    return (
      &lt;TodosProvider ingredientArray={ingredientArray}&gt;
        &lt;TodoForm/&gt;
        &lt;TodoList/&gt;
      &lt;/TodosProvider&gt;
    );
  }
  
  export default TodoApp;
  </code></pre>
  <p>And the top level component that passes the props as well:</p>
  <pre class="lang-js prettyprint-override"><code>import React, { useEffect, useContext } from 'react';
  import TodoApp from './TodoApp';
  import useStyles from '../styles/AppStyles';
  import Paper from '@material-ui/core/Paper';
  
  function App({ ingredientArray }) {
    const classes = useStyles();  
  
    return (
      &lt;Paper className={classes.paper} elevation={3}&gt;
        &lt;div className={classes.App}&gt;
          &lt;header className={classes.header}&gt;
            &lt;h1&gt;
              Shoppinglist
          &lt;/h1&gt;
          &lt;/header&gt;
          &lt;TodoApp ingredientArray={ingredientArray} /&gt;
        &lt;/div&gt;
      &lt;/Paper&gt;
    );
  }
  
  export default App;
  </code></pre>
  <p>The parent component where ingredientArray is made. It takes the last recipe in the state.recipes array and passes it as props to the shoppingList:</p>
  <pre><code>...
    const handleSetNewRecipe = (recipe) =&gt; {
      recipe.date = state.date;
      setState({ ...state, recipes: [...state.recipes, recipe] })
    }
  ...
  
   {recipesOpen ? &lt;RecipeDialog
      visible={recipesOpen}
      setVisible={setRecipesOpen}
      chosenRecipe={handleSetNewRecipe}
    /&gt; : null}
  
  ...
  
   &lt;Grid item className={classes.textAreaGrid}&gt;
       &lt;ShoppingList ingredientArray={state.recipes.length ? state.recipes.reverse()[0].ingredients : []}/&gt;
   &lt;/Grid&gt;
  
  ....
  
  </code></pre>
  <p>What am I doing wrong?</p>