Search code examples
javascriptreactjsuse-effectuse-contextuse-reducer

Why my react app rendered twice and give me an empty array in first render when I'm trying fetching API?


The second render was success but the side effect is my child component's that using context value from its parent as an initialState (using useState hooks) set its initial state to empty array. I'm using React Hooks ( useState, useContext, useReducer, useEffect)

App.js:

...
export const MainContext = React.createContext();

const initialState = {
  data: [],
};

const reducer = (state, action) => {
  switch (action.type) {
    case "ADD_LIST":
      return { ...state, data: action.payload };
    default:
      return state;
  }
};

function App() {
  const [dataState, dispatch] = useReducer(reducer, initialState);
  const fetchData = () => {
    axios
      .get("http://localhost:3005/data")
      .then((response) => {
        const allData = response.data;
        if (allData !== null) {
          dispatch({ type: "ADD_LIST", payload: allData });
        }
      })
      .catch((error) => console.error(`Error: ${error}`));
  };

  useEffect(() => {
    fetchData();
  }, []);

  console.log("useReducer", dataState); //LOG 1

  return (
    <div>
      <MainContext.Provider
        value={{ dataState: dataState, dataDispatch: dispatch }}
      >
        <MainPage />
      </MainContext.Provider>
    </div>
  );
}
export default App;

My child component MainPage.jsx

...
function MainPage() {
  const mainContext = useContext(MainContext);
  const data = mainContext.dataState.data;
  const uniqueList = [...new Set(data.map((list) => list.type))];
  console.log("uniqueList", uniqueList); // LOG 2
  const [uniqueType, setUniqueType] = useState(uniqueList);
  console.log("uniqueType State", uniqueType); // LOG 3
  const catAlias = {
    account: "Account",
    commandLine: "Command",
    note: "Note",
    bookmark: "Bookmark",
  };
  return (
  // jsx code, not necessary with the issue
  )
};

The result : result image

As you can see, uniqueType state is still empty eventhough the uniqueList is already with filled array. My goal is to make uniqueType initial state into the uniqueList from the first render.


Solution

  • That's the expected behavior. The value used for state initialization is only used in first render. In subsequent renders, the component needs to use useEffect to keep track of value changes.

    UseEffect(()=>{
     // unique list update.
    }, [data]);