3

I have just started learning react native this week and after watching some long youtube tutorials I decided to create a to do list app in which the person can add, delete and edit the generated list of things to do. Currently my code can just add and delete but I was wondering how can a edit function be added to my project, I have no idea of how to make that work so if anyone can help me with an idea on this it will be awesome.

Here is a codesandbox of my project https://codesandbox.io/s/vigilant-williamson-4zv7s?file=/src/App.js

If you have any questions please let me know in the comments

Juan Martin Zabala
  • 743
  • 2
  • 9
  • 29
  • Please, include link to https://codesandbox.io with working example of your code – macborowy Jul 30 '20 at 18:38
  • Typically you'll want an edit callback handler that takes the index or id of the data element to edit (in order to toggle *some* edit view), the edit mode view (i.e. *some* input), and a save edit handler to update *that* element in your data in state. Please try to include a [Minimal, Complete, and Reproducible](https://stackoverflow.com/help/minimal-reproducible-example) code example in your question, and if possible, a *running* codesandbox, and include a clearly as possible detail on any issues and expected result. Good luck. – Drew Reese Jul 30 '20 at 18:58
  • @DrewReese I just added the sandbox I really hope it helps, let me know if there are any issues – Juan Martin Zabala Jul 30 '20 at 20:31
  • @macborowy I just added the sandbox I really hope it helps, let me know if there are any issues – Juan Martin Zabala Jul 30 '20 at 20:32

1 Answers1

1

To make it work you need to add new edit handler, similar to pressHandler, but editing an entry instead of removing it. The possible edit handler could look like:

const editHandler = (todoKey, newText) => {
  const newTodos = [...todos];
  const index = newTodos.findIndex(todos => todos.key === todoKey);
  newTodos[index] = Object.assign(newTodos[index], { value: newText });

  setTodos(newTodos);
};

It moves the edited element at the end of the list. If you want, you can change this behavior on your own.

Then you need to pass the handler to <TodoItem />:

<TodoItem
  key={item.key}
  todoKey={item.key}
  title={item.value}
  editHandler={editHandler}
  pressHandler={pressHandler}
/>

You don't need to bind function component functions, but you need to provide a key props for every component you render in map(). I've changed it and provided a todoKey props I later use in <TodoItem />.

In <TodoItem /> you can similar logic for modifing todo text as you use in <AddTodo /> for creating new todo. I use conditional rendering to render the <TextInput /> when isEditing is true, and <Text /> when it's not.

{isEditing 
  ? <TextInput value={text} onChangeText={setText} style={styles.itemText} />
  : <Text style={styles.itemText}>{props.title}</Text>
}

Similarly, I conditionally render Save and Edit buttons.

Full <TodoItem /> component:

const TodoItem = props => {
  const [text, setText] = useState("");
  const [isEditing, setEdit] = useState(false);

  const handleEdit = () => {
    props.editHandler(props.todoKey, text);
    setText("");
    setEdit(false);
  };

  return (
    <View style={styles.items}>
      <View style={styles.itemContainer}>
        {isEditing 
          ? <TextInput value={text} onChangeText={setText} style={styles.itemText} />
          : <Text style={styles.itemText}>{props.title}</Text>
        }
        <View style={styles.btnContainer}>
          <Buttons title="Delete" onPress={() => props.pressHandler(props.todoKey)} style={styles.itemBtn} />
          {isEditing 
            ? <Buttons title="Save" onPress={handleEdit} style={styles.editBtn} />
            : <Buttons title="Edit" onPress={() => setEdit(true)} style={styles.editBtn} />
          }
        </View>
      </View>
    </View>
  );
};

const styles = /* ... */

Here is codesandbox with a code: https://codesandbox.io/s/public-edit-todo-item-bsc9p


EDIT 1

If you want to load current TODO title during edit instead of clearing it first, change <TodoItem />:

  • set props.title as initial value of text
  • remove setText("") from handleEdit - it's not needed anymore
const TodoItem = props => {
  const [text, setText] = useState(props.title);
  const [isEditing, setEdit] = useState(false);

  const handleEdit = () => {
    props.editHandler(props.todoKey, text);
    setEdit(false);
  };

  return (
    {/* stays the same */}
  )
}
macborowy
  • 1,474
  • 10
  • 13
  • Hey! So in isEditing, `?`means that the text can be changed because there is a text input and `:` means that it cant be edited because there is just text right? sorry I am very new to react, this is first week learning it – Juan Martin Zabala Jul 31 '20 at 16:56
  • I have other question, Why is the edited item moving to the last? I just need to know why – Juan Martin Zabala Jul 31 '20 at 19:53
  • `?` let you [conditionally render](https://reactjs.org/docs/conditional-rendering.html#inline-if-else-with-conditional-operator) elements, so if `isEditing` is `true` React render `` and `` otherwise. – macborowy Aug 01 '20 at 08:09
  • 1
    @JuanMartinZabala You're right, adding an item at the end of the array was unnecessary. I've added this because I can't make state updates work properly. I made mistake trying to update the state directly which is forbidden in React. I've updated the answer with the correct code. – macborowy Aug 02 '20 at 10:26
  • Hey macborowny! So basically to solve this problem, you called all the elements from todos doing this `[...todos]` and then to target the selected element to be edited you added `.findIndex(todos => todos.key === todoKey)`, and you gaved a new value to index to finally apply it to the original array using `setTodos(newTodos)` right? – Juan Martin Zabala Aug 02 '20 at 15:58
  • @JuanMartinZabala yes, you're right. `[...todos]` creates new array with all elements from the previous one. React uses [shallow comparison](https://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react) during re-renders meaning it will look if array change itself (e.g.: has new elements), not if array elements changed (e.g.: object properties changed). – macborowy Aug 02 '20 at 16:20
  • Thank you so much I really appreciate your help, I have a doubt. I tried erasing `setText(" ")` on handleEdit so that I could get the text of the element that is being edited in the text input. But it doesnt show anything except if the element has already being edited, To fix that I have to call props on todo array right? if so, how can I call props on an array function? – Juan Martin Zabala Aug 02 '20 at 16:30
  • Could you explain more about what you are trying to achieve? What behavior do you expect? – macborowy Aug 02 '20 at 18:16
  • Yes, Currently when I want to edit an item the TextInput shows no content inside of it right? Well, I want the text input to show the content of the selected item in the edit TextInput. Was that clear enought? – Juan Martin Zabala Aug 02 '20 at 18:49
  • let me know if you need more explanation – Juan Martin Zabala Aug 02 '20 at 21:37
  • In case you need more explanation, what I want to achieve is similar to when you want to edit a post in instagram, when editing a post on instagram you can see the content you had in your post in form of a textInput so that it could be edited instead of seeing nothing and trying to type the whole bio again with the respective changes – Juan Martin Zabala Aug 03 '20 at 01:12
  • Thank you macborowny! I didnt thought that was posible – Juan Martin Zabala Aug 03 '20 at 12:41