New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ReactNative] ListView.DataSource doesn't work with ObservableArrays #476
Comments
If I remember correctly (not an active RN dev myself) the ListViewDataSource itself can work with observable arrays, but you need to make sure that your renderRow={RowRenderer}
//...
const RowRenderer = observer(data => {}) Should do the trick. Let me know if it doesn't :) |
@mweststrate Thanks for your reply. Unfortunately it doesn't work for me. RowRenderer is a function that gets called with the rowData for every element in the underlying DataSource. So my code looks like this: <ListView
renderRow={rowData => (
<MyRow data={rowData} .../>
)}/> I've modified |
Does it not react to changes in the row data, or to appending / remove items to the collection? For the latter you might need |
Hi @winterbe! I've been using mobx with RN extensively, and I haven't gotten mobx "arrays" to work with datasources either. The reason might be that mobx arrays are really objects and datasource expects an array. I always call One trick is to create the datasource in a Datasource did not work with |
@mweststrate I don't know if it reacts to changes because the ListView doesn't render any rows at all when calling @danieldunderfelt Using |
Hello, @winterbe and @danieldunderfelt could anyone show me example how to use mobx with react native. |
Nothing special about Mobx with ReactNative. Just make sure to import Here's a starting guide: https://medium.com/@dabit3/react-native-with-mobx-getting-started-ba7e18d8ff44#.uge82y49s Am Sonntag, 21. August 2016 schrieb Abdulaziz Alkharashi :
|
@winterbe So which way did you go? computed or slice? and why are you looking for a replacement for Redux? I'm also evaluating the same. |
I use compute if the array presented in ListView has to be filtered first. Am Donnerstag, 1. September 2016 schrieb Ori Harel :
|
I ran into a problem when I had a list with section headers. After digging through some code, it turns out that ListViewDataSource when it was calculating rowIdentities on an ObservableArray, it does Objects.keys on it. RN expects that it would output the indexes of the array, but it doesn't because it's an Observable Array. My solution here is when I call cloneWithRowsAndSections, I have to pass in the sectionIdentities and rowIdenties myself
|
@danieldunderfelt can you share some code? the following doesn't work |
@sonayen did you try wrapping it in mobx.toJS? |
@ajma tried |
Here is my full attempt:
|
@ajma any thoughts? |
@sonayen Hi! You need to |
@danieldunderfelt finally it worked! thanks. import React, { Component } from 'react';
import { ListView, Text, TouchableOpacity, View } from 'react-native';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react/native';
class ListStore {
@observable list = [
'Hello World!',
'Hello React Native!',
'Hello MobX!'
];
ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
@computed get dataSource() {
return this.ds.cloneWithRows(this.list.slice());
}
@action add = title => this.list.push(title);
}
const listStore = new ListStore();
@observer class List extends Component {
render() {
return (
<View style={{ flex: 1, padding: 10 }}>
<ListView
dataSource={listStore.dataSource}
renderRow={row => <Text>{row}</Text>}
enableEmptySections={true}
/>
<TouchableOpacity onPress={() => listStore.add('Lorem ipsum dolor sit amet')} style={{ bottom: 0 }}>
<Text>listStore.add()</Text>
</TouchableOpacity>
</View>
);
}
}
export default List; |
@sonayen would you mind documenting the problem and solution approach here? https://github.com/mobxjs/mobx/blob/gh-pages/docs/best/pitfalls.md I think that would be really useful for people running into this in the future! |
@mweststrate sure thing, i will work on it |
It seems that the solution above doesn't work for inner object property updates. For instance, if we have this observable, instead of a plain string array: @observable list = [
{ text: 'Hello World!' },
{ text: 'Hello React Native!' },
{ text: 'Hello MobX!' }
]; Then our Now if an The renderRow function won't fire the reactions to recompute the The following hack will fire the ListView update after the item changes, but it doesn't feel right :) @computed get dataSource() {
this.list.forEach(e => e.text);
return this.ds.cloneWithRows(this.list.slice());
} Finally, this issue tells that we need to clone and update the object in the array instead of just changing it's properties. Which doesn't feel right too. Since the hack works (although it re-renders all items in the ListView), it seems that it is possible to handle it properly with mobx, right? Does anyone have a better idea on how to observe and fire the updates by using the inner property access that happens inside the |
@feroult it seems that iteration is the solution for now (es5 compatibility related). one down side also would be that you have to explicitly iterate through each object key that you wish to have its value observed: @observable list = [{ text: 'foo', subtext: 'bar' }];
@computed get dataSource() { this.list.forEach(e => [ e.text, e.subtext ]); // .. more discussion about this here. |
@feroult The reason you're not seeing updates is that Then your renderRow(row) {
return <RowComponent data={ row } />
} So it is not the datasource that is your problem at all, it is your render function. |
For an explanation, see also: https://github.com/mobxjs/mobx/blob/gh-pages/docs/best/react.md#mobx-only-tracks-synchronously-accessed-data |
I just wonder why is this closed? Is it wont fix? Is it fixed? Could it be that ListView.DataSource iterates the array using a for loop? If that is the case, I think we can fix it very easily just by making properties of |
@danieldunderfelt Extracting the component and having a change in the mobx observable array does not seem to refresh the ListView for me... Here is what I got: const dataSource = new ListView.DataSource({ rowHasChanged: ( r1, r2 ) => r1.id !== r2.id });
@observer
export default class Movies extends Component {
@computed get dataSource() {
return dataSource.cloneWithRows(MovieStore.moviesList.slice());
}
render() {
return (
<View>
<ListView
renderRow={(row) => <Row data={row} />}
dataSource={this.dataSource}
/>
</View>
)
}
} Extracted row component: @observer
export default class Row extends Component {
render() {
const movie = this.props.data;
return (
<View key={movie.id}>
<View>
<MovieItem movie={movie} />
</View>
</View>
)
}
} A movie in the initial I believe I implemented what you meant above for the ListView to reload on change of an attribute. Since the movie's attribute in |
@MovingGifts You need to also decorate your Row component with Also, you might want to do some changes to your DataSource. Right now, I don't believe that To have the function actually DO anything, I recommend comparing the I hope that makes sense! The DataSource is a bit finicky. |
@danieldunderfelt Thank you so much for getting back so soon. I updated the code above to have an I think the only thing missing is what you said here:
Do you mind sharing what that code looks like based on my code above, as I am not sure 100% what's left to implement to get it to work? |
@danieldunderfelt Do you mind sharing what that code looks like based on my code above, as I am not sure 100% what's left to implement to get it to work? |
Can you confirm that the movie objects inside the If this is the case, they should be triggering the |
@feroult Yup, they are observable
|
@MovingGifts I had the same problem. I ended up importing
Now the rows are rerendered as observable data changes. |
@danieldunderfelt @feroult @binchik Thank you so much for all your help guys. The issue was the nested component |
Hey guys!
---Todo Component----
I get this error each time I add a new Todo.
|
Hi,
Now for the component `@inject( "productStore", "app", "routerActions") } <List I am using nativebase list which is using listview internally not sure why all the stuff doesn work Also I have kept
not sure where should I call this.setsate to update changes plz help |
Hi,
I'm currently evaluating MobX to be used with React Native. It seems that
ListView.DataSource
unfortunately doesn't work natively with observable arrays from MobX. I have to create a native array viatoJS()
in order the get theListView
show any items.I've just started experimenting with MobX but I'm a little concerned that calling
toJS()
for large collections on every render could lead to performance problems.Please correct me if I'm wrong and there's another way of getting the DataSource to accept ObservableArrays.
I understand that observable types are a consequence of MobX and that you cannot ensure that every library works out of the box with those types. However in ReactNative
ListView
is such a fundamental component that I hope there's a decent solution when using MobX.Thanks.
The text was updated successfully, but these errors were encountered: