2

I am trying to create to To-Do list.

function App() {
  return (
    <div className="App">
      <UseReducerToDo/>
    </div>
  );
}

const { useReducer, useState } = React;

const reducer = (state, action) => {
  return [...state, { id: Date.now(), ToDo: action, completed: false }];
};

function UseReducerToDo() {
  const [state, dispatch] = useReducer(reducer, []);
  const [Todo, setTodo] = useState("");

  const addToTODO = (e) => {
    e.preventDefault();
    dispatch(Todo);
  };

  return (
    <div>
      <form onSubmit={addToTODO}>
        <input
          type="text"
          value={Todo}
          onChange={(e) => setTodo(e.target.value)}
        ></input>
      </form>
      {state.length > 0
        ? state.map((todo) => <DispToDo key={todo.id} todo={todo} />)
        : "no pending items"}
    </div>
  );
}

function DispToDo({ todo }) {
  console.log("DispToDo", todo);
  return (
    <div>
      <p>
        {todo.ToDo + "->"} status {todo.completed ? "done" : "pending"}
      </p>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Now, the code compiles and works fine. The issue is regarding performance.

when the application runs initial screen

when the first element is added and submitted after first element added

Now the actual issue pops up after each keypress

I have figured out that each keypress in the text box refreshes the child component DispToDo. That is evident, as logs in the console added up for every keypress.

There is no change in the state but there is refresh in DispToDo.

Is there any way to optimize this?

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153

2 Answers2

1

Try rendering a memoized component:

const MemoDispToDo = React.memo(DispToDo);

// ...
.map(todo => <MemoDispToDo key={todo.id} todo={todo} />)

Edit React Template (forked)

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
1

You can memoize your state.map() with a useMemo() hook:

const todos = useMemo(() => (
  state.map((todo) => <DispToDo key={todo.id} todo={todo} />)
), [state]);

See below for a full demo:

function App() {
  return (
    <div className="App">
      <UseReducerToDo/>
    </div>
  );
}

const { useMemo, useReducer, useState } = React;

const reducer = (state, action) => {
  return [...state, { id: Date.now(), ToDo: action, completed: false }];
};

function UseReducerToDo() {
  const [state, dispatch] = useReducer(reducer, []);
  const [Todo, setTodo] = useState("");

  const addToTODO = (e) => {
    e.preventDefault();
    dispatch(Todo);
  };
  
  const todos = useMemo(() => (
    state.map((todo) => <DispToDo key={todo.id} todo={todo} />)
  ), [state]);

  return (
    <div>
      <form onSubmit={addToTODO}>
        <input
          type="text"
          value={Todo}
          onChange={(e) => setTodo(e.target.value)}
        ></input>
      </form>
      {state.length > 0 ? todos : "no pending items"}
    </div>
  );
}

function DispToDo({ todo }) {
  console.log("DispToDo", todo);
  return (
    <div>
      <p>
        {todo.ToDo + "->"} status {todo.completed ? "done" : "pending"}
      </p>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153