reactjs - React-Native 通过 ListView 和 Navigator 传播 props 中的更改

标签 reactjs react-native

我有以下情况。我有一个包含项目列表的父组件,其中任何一个都可以深入到子组件中并在子组件中查看。从子组件中,您应该能够更改您正在查看的项目中的值。

在 React Web 世界中,这个问题很容易解决,父级将列表存储为状态,并将项目和更改回调作为 props 传递给子级。

使用 React Native,似乎失去了这种可能性,因为从子组件引起的更改在导航离开之前不会触发重新渲染。

我录制了一个视频来展示它的样子。 https://gfycat.com/GreenAgitatedChanticleer

代码如下。

index.ios.js

var React = require('react-native');
var {
    AppRegistry,
    Navigator
} = React;

var List = require('./list');

var listviewtest = React.createClass({
    render: function() {
        return (
            <Navigator
                initialRoute={{ component: List }}
                renderScene={(route, navigator) => {
                    return <route.component navigator={navigator} {...route.passProps} />;
                }} />

        );
    }
});

AppRegistry.registerComponent('listviewtest', () => listviewtest);

list.js

var React = require('react-native');
var _ = require('lodash');
var {
    View,
    Text,
    TouchableHighlight,
    ListView
} = React;

var Detail = require('./detail');

var List = React.createClass({
    getInitialState() {
        var LANGUAGES = [
            { id: 1, name: 'JavaScript' },
            { id: 2, name: 'Obj-C' },
            { id: 3, name: 'C#' },
            { id: 4, name: 'Swift' },
            { id: 5, name: 'Haskell' }
        ];

        var ds = new ListView.DataSource({ rowHasChanged: (a, b) => a !== b })
        return {
            languages: LANGUAGES,
            ds: ds.cloneWithRows(LANGUAGES)
        };
    },

    goToLanguage(language) {
        this.props.navigator.push({
            component: Detail,
            passProps: {
                language: language,
                changeName: this.changeName
            }
        });
    },

    changeName(id, newName) {
        var clone = _.cloneDeep(this.state.languages);
        var index = _.findIndex(clone, l => l.id === id);
        clone[index].name = newName;

        this.setState({
            languages: clone,
            ds: this.state.ds.cloneWithRows(clone)
        });
    },

    renderRow(language) {
        return (
            <TouchableHighlight onPress={this.goToLanguage.bind(this, language)}>
                <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', paddingTop: 5, paddingBottom: 5, backgroundColor: '#fff', marginBottom: 1 }}>
                    <Text style={{ marginLeft: 5, marginRight: 5 }}>{language.name}</Text>
                </View>
            </TouchableHighlight>
        );
    },

    render() {
        return (
            <View style={{ flex: 1, backgroundColor: '#ddd' }}>
                <Text style={{ marginTop: 60, marginLeft: 5, marginRight: 5, marginBottom: 10 }}>Select a language</Text>
                <ListView
                    dataSource={this.state.ds}
                    renderRow={this.renderRow} />
            </View>
        );
    }
});

module.exports = List;

detail.js

var React = require('react-native');
var {
    View,
    Text,
    TouchableHighlight
} = React;

var Detail = React.createClass({
    changeName() {
        this.props.changeName(this.props.language.id, 'Language #' + Math.round(Math.random() * 1000).toString());
    },

    goBack() {
        this.props.navigator.pop();
    },

    render() {
        return (
            <View style={{ flex: 1, backgroundColor: '#ddd', alignItems: 'center', justifyContent: 'center' }}>
                <Text>{this.props.language.name}</Text>

                <TouchableHighlight onPress={this.changeName}>
                    <Text>Click to change name</Text>
                </TouchableHighlight>

                <TouchableHighlight onPress={this.goBack}>
                    <Text>Click to go back</Text>
                </TouchableHighlight>
            </View>
        );
    }
});

module.exports = Detail;

最佳答案

事实证明,这种行为是故意的,至少目前是这样。这里有一个讨论线程:https://github.com/facebook/react-native/issues/795

对于任何寻找解决方法的人,我正在使用 RCTDeviceEventEmitter 在 Navigator 之间传递数据。更新了下面的代码

list.js

var React = require('react-native');
var _ = require('lodash');
var {
    View,
    Text,
    TouchableHighlight,
    ListView
} = React;

var Detail = require('./detail');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');

var List = React.createClass({
    getInitialState() {
        var LANGUAGES = [
            { id: 1, name: 'JavaScript' },
            { id: 2, name: 'Obj-C' },
            { id: 3, name: 'C#' },
            { id: 4, name: 'Swift' },
            { id: 5, name: 'Haskell' }
        ];

        var ds = new ListView.DataSource({ rowHasChanged: (a, b) => a !== b })
        return {
            languages: LANGUAGES,
            ds: ds.cloneWithRows(LANGUAGES)
        };
    },

    goToLanguage(language) {
        this.props.navigator.push({
            component: Detail,
            passProps: {
                initialLanguage: language,
                changeName: this.changeName
            }
        });
    },

    changeName(id, newName) {
        var clone = _.cloneDeep(this.state.languages);
        var index = _.findIndex(clone, l => l.id === id);
        clone[index].name = newName;

        RCTDeviceEventEmitter.emit('languageNameChanged', clone[index]);

        this.setState({
            languages: clone,
            ds: this.state.ds.cloneWithRows(clone)
        });
    },

    renderRow(language) {
        return (
            <TouchableHighlight onPress={this.goToLanguage.bind(this, language)}>
                <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', paddingTop: 5, paddingBottom: 5, backgroundColor: '#fff', marginBottom: 1 }}>
                    <Text style={{ marginLeft: 5, marginRight: 5 }}>{language.name}</Text>
                </View>
            </TouchableHighlight>
        );
    },

    render() {
        return (
            <View style={{ flex: 1, backgroundColor: '#ddd' }}>
                <Text style={{ marginTop: 60, marginLeft: 5, marginRight: 5, marginBottom: 10 }}>Select a language</Text>
                <ListView
                    dataSource={this.state.ds}
                    renderRow={this.renderRow} />
            </View>
        );
    }
});

module.exports = List;

detail.js

var React = require('react-native');
var {
    View,
    Text,
    TouchableHighlight
} = React;

var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');

var Detail = React.createClass({
    getInitialState() {
        return {
            language: this.props.initialLanguage,
            subscribers: []
        };
    },

    componentDidMount() {
        var subscriber = RCTDeviceEventEmitter.addListener('languageNameChanged', language => {
            this.setState({ language });
        });

        this.setState({
            subscribers: this.state.subscribers.concat([subscriber])
        });
    },

    componentWillUnmount() {
        this.state.subscribers.forEach(sub => {
            console.log('removing');
            sub.remove();
        });
    },

    changeName() {
        this.props.changeName(this.state.language.id, 'Language #' + Math.round(Math.random() * 1000).toString());
    },

    goBack() {
        this.props.navigator.pop();
    },

    render() {
        return (
            <View style={{ flex: 1, backgroundColor: '#ddd', alignItems: 'center', justifyContent: 'center' }}>
                <Text>{this.state.language.name}</Text>

                <TouchableHighlight onPress={this.changeName}>
                    <Text>Click to change name</Text>
                </TouchableHighlight>

                <TouchableHighlight onPress={this.goBack}>
                    <Text>Click to go back</Text>
                </TouchableHighlight>
            </View>
        );
    }
});

module.exports = Detail;

关于reactjs - React-Native 通过 ListView 和 Navigator 传播 props 中的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30791919/

相关文章:

javascript - 生成缩略图并上传到 Node 中的 s3 存储桶的最佳方法是什么?

react-native - 每次组件成为 react-native 中的事件选项卡时调用函数

react-native - 将带有标签的 Native Post 图像 react 到 Facebook

javascript - 我无法使用 React Native 的 axios API 登录

ios - 支持的方向与应用程序没有共同的方向,并且 [RCTModalHostViewController shouldAutorotate] 返回 YES

reactjs - 使用 Rendertron 渲染时样式组件的产品版本问题

javascript - React 中响应的用法

reactjs - 创建新的React应用程序后,在安装后导入软件包或将其包含在index.html中有什么区别?

javascript - react : Create onClick HOC without using an additional <div/>

react-native - 如何在 react 抽屉导航中将背景颜色更改为透明