yarliganfatih / WhatsappGPT

Imagine talking to Elon Musk via WhatsApp. The application allows you to have extraordinary experiences by having ChatGPT play the role of a famous person.
1 stars 0 forks source link

My suffering when transferring data between components #2

Closed yarliganfatih closed 1 year ago

yarliganfatih commented 1 year ago

First I created a structure like this

ChatsScreen { // parentComponent
  clickAChat(){
    nav.push(ChatScreen, {updateChats}) // so that the childComponent can use the function in the parentComponent
  }
  updateChats(_id, _lastMessage){ 
    // after search and assign
    chats[i].lastMessage = _lastMessage
    setChats([...chats]) // succesfully updated
  }
  setChats(){ // actually useState function
    // on every chats changed
    AsyncStorage.setItem(chats)
  }
}

ChatScreen { // childComponent
  sendMessage(_msg){
    route.params.updateChats(_msg)
  }
}

I haven't had any problems so far.

Then I tried to create another similar structure.

tabNavigator {
  <ChatsScreen
    headerRight = () => {
      nav.push(ContactScreen) // I can't pass any function of ChatsScreen component
    }
  />
  <ContactScreen />
}

ChatsScreen { // parentComponent
  addChat(_user){ 
    if(!chats.includes(_user)){
      let newChat = {id, _user}
      setChats([newChat, ...chats]) // FAIL: it first updates the new data, then the old data
    }
    nav.push(ChatScreen, {updateChats})
  }
  setChats(){ // actually useState function
    // on every chats changed
    AsyncStorage.setItem(chats)
  }
}

ContactScreen { // childComponent
  clickAContact(_user){
    route.params.addChat(_msg) // This method won't work because we can't pass the function to the childComponent
    // so I will decide the function to be called with parameters
    eventCaller = {"event.addChat", {param: item}}
    nav.push(ChatsScreen, {eventCaller}) // I was able to redirect but this time I couldn't send the parameters
    // so I used DeviceEventEmitter
    DeviceEventEmitter.emit(eventCaller) // when listener is inside the functional component, it is called more than once
    // so I defined it globally outside the parentComponent for the listener to be defined only once
  }
}

Everything worked fine until here, Even the new chat has been added to the chats variable. I noticed a hidden method that converts the chats variable to its previous state while it will redirect to the full chat screen. Therefore, new chat could not be added. I couldn't find the source of that method.

image

Although the chats variable changes to its previous state when calling the setChats method in the addChat function, it does not happen in the updateChats function I mentioned earlier.

yarliganfatih commented 1 year ago

As an alternative to these, I will update the data only with AsyncStorage and make it state variables that pull the data from AsyncStorage when the components are focused.

Component {
  const [data, setData] = useState(preData);
  const _setDatas = (_data) => { // _setData will now be used instead of setData in the component
    AsyncStorage.setItem("data", JSON.stringify(_data)) // for sync
    setData(_data)
  }
  const isFocused = useIsFocused();
  useEffect(() => {
    if (isFocused) {
      Promise.resolve(asyncSetData()).catch((e) => { throw e; });
    }
  }, [isFocused]);
  const asyncSetData = async() => {
    const _data = await AsyncStorage.getItem("data")
    if (_data) setData(JSON.parse(_data))
  }
}