Search code examples
javascripttypescriptreact-nativecommand-line-interfacereact-native-image-picker

Typescript React Native Image Picker double-tap issue (non expo)


So, there's some good news and some bad news.

The good news is that the code below works.

The bad news is that I have to pick the image twice before it displays client-side.

Import code: import {launchImageLibrary} from 'react-native-image-picker'

Setting states:

const [loading1, setLoading1] = useState(false)
const [ image, setImage ] = useState('')

      const [post, setPost] = useState([
        {
          title: '',
          image: '',
          user
        }
      ])

ImagePicker code:

      const uploadImage = async ( index: number )=>{ 
      setLoading1(true)
      const result = await launchImageLibrary({
        mediaType: 'mixed',
        quality: 1
      }).then(res =>{
        if(!res.didCancel){
          let data = res.uri || res.assets?.[0]?.uri
          setImage(data)
        }
        
      })
        if(image){
          setPost(prevPost =>{
            const updatedPost = [...prevPost]
            updatedPost[index] = {
              ...updatedPost[index],
              image: image 
            }
            return updatedPost 
          })   
        }
       console.log('RESULT=====> ', image)   
      setLoading1(false)
    }

The prevPost code is only for adding the image to other elements in a form post. I suspect there's something that I'm missing in the code above that causes me to have to touch the Touchable Opacity below, pick the image from the phone, post it.....TWICE.

It works the second time I do it but not the first. Obviously, what I want is to only have to touch the Touchable Opacity, pick the image, then display it the first time I run through the process. I got no idea what I'm missing here. Any advice is welcome.

Anyway, here's the return code:

            {
              post.map(( item, index )=>(
                <View key={index}>
                  <TouchableOpacity onPress={()=>uploadImage(index)}>
                    <AntDesign name="link" size={20} color="black" />
                  </TouchableOpacity>     
                 
                  {
                    item.image && (
                        <Image style={styles.nft_image} resizeMode='contain' source={{ uri: item.image }} />
                    ) 
                   } 
                 
                </View>
                
              ))
            }


Solution

  • The useState is an asyncronous function which is why you might be seeing this. Specifically, setImage does not change the state of the image immediately, so checking if(image) right after setting it might still give an empty image state.

    This can be fixed by moving the update logic of the post state into the then block of the launchImageLibrary call to make sure it is executed only after a change in the state of the image.

    try this

    const uploadImage = async (index: number) => {
      setLoading1(true);
      const result = await launchImageLibrary({
        mediaType: 'mixed',
        quality: 1,
      }).then((res) => {
        if (!res.didCancel) {
          let data = res.uri || res.assets?.[0]?.uri;
          setImage(data);
    
          // Update youre post state inside the then block
          setPost((prevPost) => {
            const updatedPost = [...prevPost];
            updatedPost[index] = {
              ...updatedPost[index],
              image: data,
            };
            return updatedPost;
          });
        }
      });
      
      setLoading1(false);
    };
    

    Other answers that might help: React Native | useState Async-Await