Benjamin Smith Max
Benjamin Smith Max

Reputation: 2748

React component does not react to mobx observable data

I am using mobX for my react native project. Please consider this store class:

class Birds {
    @observable listOne = [];
    @observable fetchingListOne = false;
    @observable fetchErrorOne = '';

    @action setListOne = () => {
        this.fetchingListOne = true;
        api.getList()
            .then((data) => {
                this.listOne.replace(data);
                this.fetchingListOne = false;
            })
            .catch((error) => {
                this.fetchingListOne = false;
                this.fetchErrorOne = error;
            });
    };
}

And this the react component:

@inject('BirdStore') @observer
export default class Flat extends React.Component {
    componentDidMount() {
        this.props.BirdStore.setListOne();
    }

    _renderHeader = () => {
        return <Text style={styles.listHeaderText}>
            Hello {this.props.BirdStore.listOne.length} is {this.props.BirdStore.fetchingListOne.toString()}
            </Text>;
    };

    _renderItem = ({item}) => {
        return <Text style={styles.item}>{item.name}</Text>
    };

    _renderFooter = () => {
        if (this.props.BirdStore.fetchingListOne) {
            return <ActivityIndicator/>
        }
        else {
            return null
        }
    };

    render() {
        const dataSource = this.props.BirdStore.listOne.slice();

        return (
                <View style={styles.container}>
                    <Text>Fetching: {this.props.BirdStore.fetchingListOne.toString()}</Text>
                    <FlatList
                        style={styles.listContainer}
                        ListHeaderComponent={this._renderHeader}
                        data={dataSource}
                        renderItem={this._renderItem}
                        keyExtractor={(item, i) => item.id}
                        ListFooterComponent={this._renderFooter}
                    />
                </View>
        )
    }
}

From above it looks to me that:

  1. When the Flat component mounts, it call the method of the store setListOne().
  2. setListOne() sets fetchingListOne to true and makes an api call.
  3. On the component side, when the fetchingListOne is true, the ActivityIndicator displays, and in the ListHeaderComponent it should display true.
  4. On the store side, after successful/unsuccessful response, it sets fetchingListOne to false.
  5. Finally on the component side, because fetchingListOne is set to false, ActivityIndicator should not display and in the ListHeaderComponent it should display false.

However, this is not what's happening. Here when the setListOne() method is called, after it sets the fetchingListOne to true, the component does not react to the changes made after api call. And the ActivityIndicator keeps displaying and in ListHeaderComponent its displaying true.

What am I doing wrong here? Could you please help me. Thank you

Update

I have added a Text component before the FlatList. Adding a Text component or console logging inside the component class's render method does makes the FlatList react to the changes. I don't know why this is happening though.

Upvotes: 4

Views: 1468

Answers (1)

mweststrate
mweststrate

Reputation: 4978

The problem you are running into here most probably, is that although Flat is an observer component, FlatList is not (it's an built-in component after all). In this setup _renderFooter and the others are part are rendered by render of FlatList, but not of FlatList. Hence they are not part of the lifecycle of Flat, but of FlatList and as such are not tracked by Mobx

There are two ways to fix this, both pretty simple:

1) declare _renderItem as observer component:

_renderItem = observer(({item}) =>
    <Text style={styles.item}>{item.name}</Text>
);

2) use an inline anonymous Observer component:

_renderItem = ({item}) => 
    <Observer>{
        () => <Text style={styles.item}>{item.name}</Text>}
    </Observer>

Upvotes: 5

Related Questions