2
votes

Before you read ->> I did try using the extraData props in my FlatList configuration but it does not seem to work properly (maybe because I'm using Redux to manage state?)

My FlatList renders data from my API (basically it displays user chat messages).

I have been able to make the FlatList refresh when each chat message is sent with this lifecycle method:

    componentDidUpdate(prevProps) {
    if (prevProps.messages !== this.props.messages) {
        this.props.fetchActivityMessages(this.props.navigation.state.params.activityId);
        }
    }

On the outside it seems to work fine. However this causes multiple/infinite requests to my server for the data. For instance, if I console.log(this.props) on the chat screen, it continues to console.log infinitely.

This is obviously a major problem but it's the only way I found to make my list refresh with the new data without having to reload the screen.

When I attempted using the extraData prop, I tried the following:

extraData={() => {this.props.fetchActivityMessages()}}
extraData={this.props}
extraData={this.props.messages}

None of which made the FlatList refresh without having to reload the page.

Can anyone help? I feel like I'm either doing something wrong with the componentDidUpdate lifecycle method (causing it to make infinite requests) or I'm not using the extraData prop correctly.

My FlatList(Chat) Component

import { activityMessage, sendActivityMessage, fetchActivityMessages } from '../actions';
import ChatListItem from './ChatListItem';

const ROOT_URL = 'https://mydomain.herokuapp.com';
const io = require('socket.io-client/dist/socket.io');
const socket = io.connect(ROOT_URL);


class ActivityChatForm extends Component {

componentDidMount() {
    this.props.fetchActivityMessages(this.props.navigation.state.params.activityId);
}

// Need to fix as it makes infinite requests to the server 
//but so far the only way I could get the FlatList to refresh without reloading page

componentDidUpdate(prevProps) {
    if (prevProps.messages !== this.props.messages) {
        this.props.fetchActivityMessages(this.props.navigation.state.params.activityId);
    }
}

handleSubmit = () => {
    const { activityId } = this.props.navigation.state.params;
    const { sendActivityMessage, messageBody } = this.props;

    socket.emit('createMessage', {
        from: 'MEE!!',
        text: messageBody
    }, (data) => {
        console.log('Received it', data);
      });

      sendActivityMessage({
          activityId,
          messageBody
      });
 }


renderItem = (message) => {
    return <ChatListItem message={message} navigation={this.props.navigation}/>;
}


renderList = () => {
        return (
            <View>
                <FlatList
                    ref={(ref) => { this.flatListRef = ref; }}
                    data={this.props.messages}
                    renderItem={this.renderItem}
                    keyExtractor={(message, index) => index}
                />
            </View>
        );
}


render() {
    return (
        <View>
            <CardSection>
                {this.renderList()}
            </CardSection>

            <CardSection>
                <Input 
                    value={this.props.messageBody}
                    onChangeText={text => this.props.activityMessage({ prop: 'messageBody', value: text})}
                    placeholder="Type your message"
                />
            </CardSection>

            <CardSection>
                <Button                     
                    onPress={() => this.handleSubmit()}
                    buttonText="Send Message"
                />
            </CardSection>
        </View>
    );
  }
}


const mapStateToProps = (state) => {
    const { messageBody } = state.activityMessage;
    const { messages, loading } = state.fetchActivityMessages;

    return { messageBody, messages, loading };
};

export default connect(mapStateToProps, { 
    activityMessage,
    sendActivityMessage,
    fetchActivityMessages
})(ActivityChatForm);

My ChatListItem component (used to renderItem in FlatList)

class ChatListItem extends React.PureComponent {

    render() {
        const { messageBody } = this.props.message.item;

        return (
            <View>
                <CardSection>
                    <Text style={styles.titleStyle}>{ messageBody }</Text>
                </CardSection>      
            </View>
        );
    }
 }

export default ChatListItem;

The fetchActivityMessages action creator. This is used to pull user message data from my API

export const fetchActivityMessages = (activityId) => {
    return async (dispatch) => {
        try {
            dispatch({ type: FETCH_MESSAGES_INITIATE });

            let token = await AsyncStorage.getItem('token');

            let { data } = await axios.get(`${ROOT_URL}/activities/${activityId}/chat`, { 
            headers: { 'x-auth': `${token}` } 
        });


            dispatch({ 
                type: FETCH_MESSAGES_SUCCESS,
                payload: data
            });         

    } catch(error) { 
            if (error.response) {
                console.log(error.response.data);
                console.log(error.response.status);
                console.log(error.response.headers);
            } else if (error.request) {
                console.log(error.request);
            } else {
                console.log('Error', error.message);
            }
            console.log(error.config);
        };
    };
};
1

1 Answers

1
votes

First get rid of the "componentDidUpdate()". Like you said that is triggering constantly. This is because on every action thats performed on the screen, its calls "componentDidUpdate".

Second the extraData attribute needs a state object attribute to be passed into it, not props. With props it will do nothing. So in your code i think you need to do the following:

state = {};
 ...
 componentDidMount() {
 const myData = this.props.fetchActivityMessages
              (this.props.navigation.state.params.activityId);
 this.setState({
    data: myData
 })
}

....
<FlatList
 ref={(ref) => { this.flatListRef = ref; }}
 data={this.props.messages}
 extraData={this.state.data}
 renderItem={this.renderItem}
 keyExtractor={(message, index) => index}
/>

I say i think because i am not sure about the redux section. Redux manages your apps state, but i think you can still have state objects set within individual screens. Again i am not fully sure. We use React-Native on a daily basis were i work, but not Redux. Hope some of this helps :)