강의

멘토링

로드맵

인프런 커뮤니티 질문&답변

김수환님의 프로필 이미지
김수환

작성한 질문수

질문입니다 ㅠㅠ TypeError: Invalid attempt to spread non-iterable instance.

해결된 질문

작성

·

1.1K

·

수정됨

1

const [editText, setEditText] = useState(""); 

 const addToDo = async () => {
    if (text === "") {
      return;
    }
      const newToDos = {
      ...toDos,
      [Date.now()]: { text, working, done: false, isEdit: false }
    };

    setToDos(newToDos);
    await saveToDos(newToDos);
    setText("");
  };


const edit = (key) => {
    const newTodos = [...toDos];
    newTodos[key].isEdit = !newTodos[key].isEdit;
    setTodos(newTodos);
  };

  const editToDos = (key) => {
    const newTodos = [...toDos];
    newTodos[key].text = editText;
    newTodos[key].isEdit = false;
    setTodos(newTodos);
    seteditText("");
  };

   {toDos[key].isEdit ? (
              <TextInput
                  style={styles.editInput}
                  onSubmitEditing={() => editToDos(key)}
                  defaultValue={toDos[key].text}
                  onChangeText={setEditText}
                  returnKeyType="done"
                />) : (
                  <Text
                    style={
                      toDos[key].done
                        ? {
                            ...styles.toDoText,
                            textDecorationLine: "line-through",
                          }
                        : styles.toDoText
                    }
                  >
                    {toDos[key].text}
                  </Text>
                )
              }
              <TouchableOpacity onPress={() => edit(key)}>
                    <AntDesign name="edit" size={24} color="white" />
              </TouchableOpacity>
              <TouchableOpacity onPress={() => deleteToDo(key)}>
                <Fontisto name="trash" size={18} color={theme.grey} />
              </TouchableOpacity>
            </View>
          ) : null
        )}
      </ScrollView>

 


KakaoTalk_20231207_172214578.jpg

 

편집버튼을 누르면 edit함수를 실행시켜 isEdit을 true로 바꿔 편집을 하는 기능을 만들고 있습니다.

첫번째 문제는 원래 있던 todo의 text와 똑같은, 편집에 대한 textinput의 text가 떠있습니다.
그리고 원래는 편집 버튼을 누르면 오른쪽에 있는 편집에 대한 text에 textinput 창이 띄워지고
왼쪽에 있는 text는 그대로 있는 상태에서 done을 눌러도 오류가 떴었구요.

코드를 좀 수정했더니 지금은 편집버튼을 누르기만 해도

TypeError: Invalid attempt to spread non-iterable instance.

In order to be iterable, non-array objects must have a [Symbol.iterator]() method., js engine: hermes

이러한 오류가 발생합니다.

deletetodo는 제대로 작동하고 있습니다.

어디가 문제인걸까요 ..ㅠ

아래는 import 와 style 에 관한 코딩을 제외한 전문입니다.

 

 

export default function App() {
  
  const [working, setWorking] = useState(true);
  const [text, setText] = useState("");
  const [toDos, setToDos] = useState({});
  const [editText, setEditText] = useState("");
  
  useEffect(() => {
    loadToDos();
    loadMode();
  }, []);
  
  const travel = () => {
    setWorking(false)
    saveMode(false)
  };

  const work = () => {
    setWorking(true)
    saveMode(true)
  };

  const edit = (key) => {
    const newTodos = [...toDos];
    newTodos[key].isEdit = !newTodos[key].isEdit;
    setTodos(newTodos);
  };

  const editToDos = (key) => {
    const newTodos = [...toDos];
    newTodos[key].text = editText;
    newTodos[key].isEdit = false;
    setTodos(newTodos);
    seteditText("");
  };

  const onChangeText = (payload) => setText(payload);

  
  const saveMode = async(mode) => {
    try {
      await AsyncStorage.setItem("@mode",JSON.stringify(mode))
    } catch(error) {
      console.log(error);
    }
  };

  const loadMode = async () => {
    const m = await AsyncStorage.getItem("@mode")
    setWorking(JSON.parse(m))
  };

  const saveToDos = async (newToDos) => {
    try {
    await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(newToDos));
  } catch(e) {  alert("Error!")
  }
};


  const loadToDos = async () => {
    const s = await AsyncStorage.getItem(STORAGE_KEY);
    console.log(s);
    setToDos(JSON.parse(s))
  };

  const addToDo = async () => {
    if (text === "") {
      return;
    }
      const newToDos = {
      ...toDos,
      [Date.now()]: { text, working, done: false, isEdit: false }
    };

    setToDos(newToDos);
    await saveToDos(newToDos);
    setText("");
  };

  const changeDone = async (key) => {
    const newToDos = {
      ...toDos,
      [key]: { ...toDos[key], done: !toDos[key].done },
    };
    setToDos(newToDos);
    await saveToDos(newToDos);
  };

  const deleteToDo = (key) => {
    Alert.alert("Delete To Do", "Are you sure?", [
      { text: "Cancel" },
      {
        text: "I'm Sure",
        style: "destructive",
        onPress: () => {
          const newToDos = { ...toDos };
          delete newToDos[key];
          setToDos(newToDos);
          saveToDos(newToDos);
        },
      },
    ]);
  };
  
  return (
    <View style={styles.container}>
      <StatusBar style="auto" />
      <View style={styles.header}>
      <TouchableOpacity onPress={work}>
          <Text
            style={{ ...styles.btnText, color: working ? "white" : theme.grey }}
          >
            Work
          </Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={travel}>
          <Text
            style={{
              ...styles.btnText,
              color: !working ? "white" : theme.grey,
            }}
          >
            Travel
          </Text>
        </TouchableOpacity>
      </View>
      <TextInput
       onSubmitEditing={addToDo}
        onChangeText={onChangeText}
        returnKeyType="done"
        value={text}
        placeholder={
          working ? "What do you have to do?" : "Where do you want to go?"
        }
        style={styles.input}
      />
       <ScrollView>
       {Object.keys(toDos).map((key) =>
          toDos[key].working === working ? (
            <View style={styles.toDo} key={key}>   
           {toDos[key].done ? (
                  <TouchableOpacity onPress={() => changeDone(key)}>
                    <Fontisto
                      name="checkbox-active"
                      size={18}
                      color="red"
                    />
                  </TouchableOpacity>
                ) : (
                  <TouchableOpacity onPress={() => changeDone(key)}>
                    <Fontisto
                      name="checkbox-passive"
                      size={18}
                      color={theme.grey}
                    />
                  </TouchableOpacity>
                )}
              <Text style={styles.toDoText}>{toDos[key].text}</Text>
              
              {toDos[key].isEdit ? (
              <TextInput
                  style={styles.editInput}
                  onSubmitEditing={() => editToDos(key)}
                  defaultValue={toDos[key].text}
                  onChangeText={setEditText}
                  returnKeyType="done"
                />) : (
                  <Text
                    style={
                      toDos[key].done
                        ? {
                            ...styles.toDoText,
                            textDecorationLine: "line-through",
                          }
                        : styles.toDoText
                    }
                  >
                    {toDos[key].text}
                  </Text>
                )
              }
              <TouchableOpacity onPress={() => edit(key)}>
                    <AntDesign name="edit" size={24} color="white" />
              </TouchableOpacity>
              <TouchableOpacity onPress={() => deleteToDo(key)}>
                <Fontisto name="trash" size={18} color={theme.grey} />
              </TouchableOpacity>
            </View>
          ) : null
        )}
      </ScrollView>
    </View>
  );
}

답변 1

1

안녕하세요, 인프런 AI 인턴입니다.

김수환님의 문제를 살펴보니 TypeError: Invalid attempt to spread non-iterable instance. 오류는 스프레드 구문을 사용할 때 해당되는 객체가 반복 가능한(iterable) 객체가 아닐 때 발생합니다. JavaScript에서는 배열이 반복 가능한 객체입니다.

제시해주신 코드에서 오류가 발생하는 부분은 const newTodos = [...toDos];와 같이 toDos를 배열로 취급하여 스프레드하려고 시도한 부분입니다. 하지만 toDos{}로 초기화되어 있어 객체이며, 이를 배열로 스프레드할 수 없습니다.

해결을 위해서는 toDos 객체 내에 있는 특정 키의 값을 수정하는 방법을 사용해야 합니다. 수정된 코드는 아래와 같이 될 것입니다:

const edit = (key) => {
  const newTodos = {
    ...toDos,
    [key]: { ...toDos[key], isEdit: !toDos[key].isEdit }
  };
  setToDos(newTodos);
};

const editToDos = (key) => {
  const newTodos = {
    ...toDos,
    [key]: { ...toDos[key], text: editText, isEdit: false }
  };
  setToDos(newTodos);
  setEditText("");
};

setToDos 함수를 사용하여 toDos 상태를 업데이트할 때, 각 키에 대응하는 객체의 프로퍼티만 수정하고 있습니다. 이렇게 수정하면 비구조화 할당을 배열이 아닌 객체에 적절하게 사용할 수 있으므로 위에서 언급된 오류가 해결될 것입니다.

추가적으로, 코드에 setTodos라는 오타가 있습니다. setToDos를 올바르게 사용해야 합니다.

코드를 수정해서 다시 시도해보시기 바랍니다.

김수환님의 프로필 이미지
김수환

작성한 질문수

질문하기