React-native-gifted-chat: bubble messages within renderBubble function can't re-rendered after setState

Created on 30 Jan 2019  路  20Comments  路  Source: FaridSafi/react-native-gifted-chat

Hi every one, I want to animate a progressbar for every sound message duration when gets played, I have a PLAY ICON for any sound message, when I click on play icon, sound starts as well and I update the progressbar by setState, actually the progress gets update but the problem is my progressbar doesnt re-render to get progress value for progressbar. and also the play icon does not re-render to get red color.

I got something from:

Jordan Daniels

my code is:

render(){
 return (
   <GiftedChat
         messages={this.state.messages}
          onSend={messages => this.onSend(messages)}
          user={{
              _id: this.state.userId,
           }}
           renderBubble={this.renderBubble}
           renderInputToolbar={this.renderInputToolbar.bind(this)}
      />
 );
}

renderBubble = props => {
        if (props.currentMessage.audio) {
            return (
                <View style={[{ width: 150, height: 70, backgroundColor: 'lightgray' }, props.position === 'left' ? { left: -41 } : {}]}>
                    <EIcon
                        name="google-play"
                        size={30}
                        color={this.state.playAudio ? "red" : "blue"}
                        style={{
                            left: 90,
                            position: "relative",
                            shadowColor: "#000",
                            shadowOffset: { width: 0, height: 0 },
                            shadowOpacity: 0.5,
                            backgroundColor: "transparent"
                        }}
                        onPress={() => {
                            this.setState({
                                playAudio: true
                            });
                            const sound = new Sound(props.currentMessage.audio, "", error => {

                                if (error) {
                                    console.log("failed to load the sound", error);
                                }

                                const duration = sound.getDuration();
                                const progressPhase = 1 / duration;

                                if (duration !== 0) {
                                    this._interval = setInterval(() => {

                                        this.setState({
                                            progress: this.state.progress += progressPhase
                                        });

                                        if (this.state.progress >= 1) {
                                            clearInterval(this._interval);
                                            this.setState({
                                                progress: 0.0,
                                                playAudio: false
                                            });
                                        }

                                    }, 1000);
                                }

                                sound.play(success => {
                                    console.log(success, "success play");
                                    if (!success) {
                                        Alert.alert("There was an error playing this audio");
                                    }
                                });

                            });
                        }}
                    />


                    <Progress.Circle progress={this.state.progress} showsText size={35} />

                </View>
            );
        } else {
            return (
                <Bubble
                    {...props}
                    textStyle={{
                        right: {
                            color: '#fff',
                        },
                        left: {
                            color: '#fff',
                        },
                    }}
                    wrapperStyle={{
                        left: {
                            backgroundColor: "orange",
                            left: -41
                        },
                        right: {
                            backgroundColor: 'green'
                        }
                    }}
                />
            );
        }
    }

My Expections

I want to re-render progressbar with updating state { progress: 0.0 } when I call

 this.setState({
           progress: this.state.progress += progressPhase
 });

// this should be re-render on every update
<Progress.Circle progress={this.state.progress} showsText size={35} />

Additional Information

  • Nodejs version: v11.3.0
  • React version: 16.6.3
  • React Native version: 0.57.8
  • react-native-gifted-chat version: 0.7.2
  • Platform(s) (iOS, Android, or both?): on macos for Android
wontfix

Most helpful comment

Sorry about that.
Maybe try to use extraData props?

All 20 comments

Actually I found the problem but still I dont know what is the solutions,
The problem is with react-native-gifted-chat version 0.7.2
The above code works well on react-native-gifted-chat version 0.4.3

In version 0.4.3 there when I click play icon all sound message progessbar starts progressing instead of the one I clicked. I want just the one should start progressing which I have clicked.

Sorry about that.
Maybe try to use extraData props?

Hi thank you for replying, could you please let me know how can I use extraData in react-native-gifted-chat?

best regrads

On Feb 2, 2019, at 6:20 PM, Xavier Carpentier notifications@github.com wrote:

@xcarpentier thank you, my problem has solved by adding extraData={this.state} to Giftedchat
and now please help me how can I start just one progress at a time?
Because now it gets update but by clicking one sound message just the last sound message gets update and re-rendered.

Easy way to understand what is my question play the following video and see what is my problem. https://vimeo.com/315390850

Can you share your code somewhere?

Hi @xcarpentier,
Now I solved this problem by conditional rendering Also I changed following class and I added
this line: _next.audio == current.audio ||_

export default class Message extends React.Component {

  shouldComponentUpdate(nextProps) {
    const next = nextProps.currentMessage;
    const current = this.props.currentMessage;
    const { nextMessage } = this.props;
    const nextPropsMessage = nextProps.nextMessage;
    return (
      next.send !== current.send ||
      next.received !== current.received ||
      next.pending !== current.pending ||
      next.createdAt !== current.createdAt ||
      next.text !== current.text ||
      next.image !== current.image ||
      next.video !== current.video ||
      next.audio == current.audio ||  // this line added by me
      nextMessage !== nextPropsMessage
    );
  }

 //..............

}

I add this condition and please please let me know about performance.
this tree line, I added to my code:

_this.myPro = <Progress.Circle progress={this.state.progress} showsText size={35} />; this.setState({currentPlayedMessage: props.currentMessage._id }); { props.currentMessage._id === this.state.currentPlayedMessage ? this.myPro : null }_

All code:

if (props.currentMessage.audio) {

            this.myPro = <Progress.Circle progress={this.state.progress} showsText size={35} />; // this line added

            return (
                <View style={[{ width: 150, height: 70, backgroundColor: 'lightgray' }, props.position === 'left' ? { left: -41 } : {}]}>
                    <EIcon
                        name="google-play"
                        size={30}
                        color={this.state.playAudio ? "green" : "blue"}
                        style={{
                            left: 90,
                            position: "relative",
                            shadowColor: "#000",
                            shadowOffset: { width: 0, height: 0 },
                            shadowOpacity: 0.5,
                            backgroundColor: "transparent"
                        }}
                        onPress={() => {

                            this.setState({
                                playAudio: true,
                                currentPlayedMessage: props.currentMessage._id // this line added
                            });

                            const sound = new Sound(props.currentMessage.audio, "", error => {

                                if (error) {
                                   return;
                                }

                                const duration = sound.getDuration();
                                const progressPhase = 1 / duration;

                                if (duration !== 0) {
                                    this._interval = setInterval(() => {

                                        this.setState({
                                            progress: this.state.progress += progressPhase
                                        });

                                        if (this.state.progress >= 1) {
                                            clearInterval(this._interval);
                                            this.setState({
                                                progress: 0.0,
                                                playAudio: false
                                            });
                                        }

                                    }, 1000);
                                }

                                sound.play(success => {
                                    console.log(success, "success play");
                                    if (!success) {
                                        Alert.alert("There was an error playing this audio");
                                    }
                                });

                            });
                        }}
                    />


                    { props.currentMessage._id === this.state.currentPlayedMessage ? this.myPro : null } // this line added

                </View>
            );
        }

Should be next.audio !== current.audio?
Because should update is audio is different.

@xcarpentier thank you to replying, I tried that, but unfortunatly it just work with last audio message has came.

my audio message is like this:

this.setState(previousState => ({
                        messages: GiftedChat.append(previousState.messages, {
                            _id: data.id,
                            audio: data.audio,
                            messageType: 'audio',
                            createdAt: new Date(),
                            user: {
                                _id: '2'
                            },
                            renderAvatar: null
                        }),
                    }));

I mean if I put it like this:
next.audio !== current.audio
there the last audio message gets render.
but if I put like this:
next.audio == current.audio

all audio messages gets render.

Hi, I dont know this line is send or sent
next.send !== current.send ?
next.sent !== current.sent ?

I will change it...

I'm having the same issue with only the last message re-rendering. Using the latest commit hash as my package.

Solved this issue by forking and implementing a special shouldUpdateMessage function. This allows for special use cases around when to re-render messages.

https://github.com/mykewould/react-native-gifted-chat/commit/058398d198aa6b59aa7b07a5f7edf035270f49b7

Let me know if you'd like to me to PR this. Feel free to steal the code.

馃憤
Yes please create a PR
@mykewould

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SytzeAndr picture SytzeAndr  路  3Comments

tafelito picture tafelito  路  3Comments

radvc picture radvc  路  3Comments

maharjanaman picture maharjanaman  路  3Comments

iamdurui picture iamdurui  路  3Comments