React Native Pass data between sibling views
Asked Answered
V

2

16

I'm developing a simple todo-list app using React Native, my issue is the following: I have a NavigatorIOS at the root of my project, with a component containing a ListView as initial route, and a navigation bar button that leads to a task creation view.

Once a new task has been created, the view is pop so that the ListView is displayed. I'm trying to add my newly created task to this ListView (its data source is contained in the component state).

How to perform such an operation, what's the good practice? I would use a delegate in the pure native app but here, both views are handled by the NavigatorIOS instance.

index.ios.js

addTask() {
    console.log("Test");
},

render() {
        return (
            <React.NavigatorIOS
                ref="nav"
                style={styles.container}
                tintColor="#ED6063"
                initialRoute={{
                    title: "Tasks",
                    component: TasksList,
                    rightButtonTitle: 'Add',
                    onRightButtonPress: () => {
                        this.refs.nav.navigator.push({
                            title: "New task",
                            component: NewTask,
                            passProps: {
                                onTaskAdded: this.addTask
                            },
                            leftButtonTitle: "Cancel"
                        });
                    }
                }}/>
        );
    }

NewTask.js

taskAdded() {
console.log("Added: " + this.state.title + " - " + this.state.description);
this.props.onTaskAdded({
    title: this.state.title,
    description: this.state.description
});
this.props.navigator.pop();
}

TasksList.js

var dataSource = new ListView.DataSource({
    rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
    dataSource: dataSource.cloneWithRows(data)
};

You can find the complete source code here.

Villainy answered 14/4, 2015 at 8:2 Comment(0)
B
15

The React-Native documentation has a brief section on approaches to communicating between components.

When you're trying to do something more complicated than a parent->child or child->parent relationship, there are a few options:

  1. Manager pattern. For true sibling<->sibling communications (i.e. where the two siblings share a parent via composition), you can have the parent manage the state. For example, you might have a <MyConsole> widget that has a <TextInput> and a <ListView> containing the past inputs by a user, both are children of the <Console> widget.

    • Here, the <Console> can act as a manager. When the <TextInput>changes its value, you can use the onChangeText event to pass the new value up to the parent <MyConsole> component, which then updates its state and passes that onto its children.
  2. Event (publish-subscribe) pattern. Remember that components are just objects, and so you can use object oriented approaches to communicating between components. The React documents note that:

    For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillUnmount(), and call setState() when you receive an event.

    • Here, you can use a simple publish-subscribe library like pubsub.js so that when one component changes it just publishes the change and other related components can listen for the event and update themselves. This can be a very effective approach for smaller apps.
  3. Flux pattern. One of the drawbacks with a pure publish/subscribe system is, it becomes difficult to keep track of state. For example, if you have 2 components (e.g. EditTitle, EditBody) which can both update some state like an email message, then a pure eventing system ends up passing different versions of state around which can get messy with conflicts because there is no "single version of the truth". This is where React's flux approach comes in. With flux, components update a data store which is responsible for updating and reconciling data (e.g. EmailDataStore), and the store then notifies components of the updated state.

    • So in your example, the task view would issue an update (e.g. via publish, or direct function invocation) to a TasksDataStore, which might then publish an event like tasks-updated to its subscribers. Both the tasks panel and the results panel would subscribe to the data store.

When setting up subscriptions, it's best to add subscriptions after the component mounts and definitely remove them before the component unmounts (otherwise you end up with a lot of orphaned subscriptions).

Belayneh answered 9/10, 2015 at 16:10 Comment(1)
tks @tohster, you are right, set up a global event system is the best way, but i am new to react-native, i don't know how to make a global event system in the react-native.Boloney
H
0

You should rewrite your constructor function to get the data from a dynamic way. Then when the page reloads, it would get a correct data which includes the new task. Here you get the data from a static array, which would not change.

Save tasks list to local file or Firebase, and read when construct.

Hulbard answered 4/10, 2015 at 2:14 Comment(4)
So you suggest to use some kind of huge global variable/area to pass data between views ? That sounds like very bad architecture...Assassin
@Assassin Otherwise, how do you get the task list? "Global" is optional.Hulbard
That exactly the point here. While developing native, we can use protocol or passing blocks of code to execute (like callback). So the task list is "shared" only with these two views, it's not on a big common place with other things.Assassin
@Assassin You can pass the list into next view as a prop.Hulbard

© 2022 - 2024 — McMap. All rights reserved.