【问题标题】:React-Native - Dynamic State from JSON for SwitchReact-Native - 来自 JSON 的用于 Switch 的动态状态
【发布时间】:2016-06-23 11:11:03
【问题描述】:

你好 :) 我通过向我的 SearchView 添加过滤器模态得到以下问题

我构建了一个SearchPage,其中可以列出几个事件。这一切都很好。现在我正在尝试将filter 添加到我的SearchPage。如果我手动设置过滤器,它工作得很好->现在我的问题:

如果我尝试更改 Switch 的开关值,它会设置回根目录,因为该值的状态未设置

我解释的步骤:

我正在尝试打开一个Modal 查看我的所有filter 都在哪里列出,我可以使用Switch 在哪里设置true/false。我的想法是通过为它创建一个JSON 来获取所有filter Settings

module.exports = {
  "filter":
      {
          "track": [
              {
                  "id": 1,
                  "description": "IoT & Living tomorrow"
              },
              {
                  "id": 2,
                  "description": "Smart & Digital Retail"
              },
              {
                  "id": 3,
                  "description": "Startups, Digital Culture & Collaboration"
              }
          ]
        }
  }

上面的 JSON 只是为了举例 - 通常它比跟踪要大得多,主题也更多

现在我导入JSON 并将其保存在var filter。我在这里检查了数据的格式是否正确-> filter.track -> All my JSON Objects

现在我用filter Modal创建了一个我的班级

import React, {Component} from 'react';
import {
    ListView,
    Modal,
    StatusBar,
    StyleSheet,
    Text,
    TouchableOpacity,
    View,
    Switch
} from 'react-native';

var filter = require('../JSON/filter');

class PopoverFilter extends Component {

    constructor(props) {
        super();
        // ds for the menu entries
        var ds = new ListView.DataSource({rowHasChanged:   (r1, r2) => r1 !== r2});
        this.state = {
            eventTracks: ds.cloneWithRows(filter.filter.track)
        }
        this.show = this.show.bind(this);
    }

    render() {
        return(
            <Modal>
                         <ListView
                                style={styles.mainView}
                                renderRow={this.renderMenuEntries.bind(this)}
                                dataSource={this.state.eventTracks}/>
                        
            </Modal>
        );
    }

    renderMenuEntries(entry) {
        var switchState = entry.description;
        return(
            <View style={styles.switchView}>
                <Text style={[styleHelper.fonts.titleSize, styles.text]}>{entry.description}</Text>
                <Switch onValueChange={(value) => this.switchChanged(switchState, value)}
                value={this.state.switchState}/>
            </View>
        );
    }


    switchChanged(field, value) {
        var obj = {};
        obj[field] = value;
        this.setState(obj);
    }
}

var styles = StyleSheet.create({
    
});

module.exports = PopoverFilter;

请忽略缺少的样式,并且模态中还有更多对象,但对于这种情况并不重要。

最重要的是,我尝试通过 renderMenuEntries 方法渲染每个 Switch,并为它们提供所有条目 -> 只是 Switch 设置不正确。至于我试图改变开关的值,它会立即回到它的根。并且没有设置任何状态。

也许我的解决方案是不可能的,我必须将每个状态都设为静态 - 但如果我以后可以在不更改整个代码的情况下设置动态过滤器,这个解决方案会非常好

【问题讨论】:

    标签: json filter react-native state


    【解决方案1】:

    你描述的场景是可能的。我在使用您的代码时遇到了许多问题:

    1. renderMenuEntries 中,您分配给&lt;Switch /&gt; 组件的值是数据项的描述,而不是&lt;Switch /&gt; 组件value 的预期布尔值预计。此外,此值还引用了不存在的 this.state 属性。

    2. switchChanged 函数也只是使用数据项的描述

    3. 更新组件状态

    使用您提供的代码示例,我从头开始创建了一个名为 PopoverFilter 的新类。它不需要此组件中的过滤器数据,而是希望数据通过名为 filterData 的组件 prop 进入。这将提高组件的可重用性以接受不同的数据集。

    代码被大量注释以帮助解释所展示的概念。这是PopoverFilter 类:

    import React from 'react';
    import {
      ListView,
      Modal,
      Switch,
      Text,
      TouchableOpacity,
      View
    } from 'react-native';
    
    export default class PopoverFilter extends React.Component {
      constructor (props) {
        super(props);
    
        // bind relevant handlers up front in the constructor
        this.renderRow = this.renderRow.bind(this);
        this.onPress = this.onPress.bind(this);
    
        // process the incoming filter data to add a 'selected' property
        // used to manage the selected state of its companion switch
        this._filterData = this.processFilterData(this.props.filterData);
    
        const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    
        this.state = {
          filterDataSource: ds.cloneWithRows(this._filterData)
        }
      }
    
      processFilterData (filterData) {
        // don't mutate the filterData prop coming in
        // use map to create a new array and use Object.assign to make
        // new object instances with a new property named 'selected' initialized
        // with a value of false
        return filterData.map((item) => Object.assign({}, item, { selected: false }));
      }
    
      switchChanged (rowId, isSelected) {
        const index = +rowId; // rowId comes in as a string so coerce to a number
        const data = this._filterData;
    
        // don't mutate this._filterData
        // instead create a new array and new object instance
        this._filterData = [
          ...data.slice(0, index), // take everything before the target index
          Object.assign({}, data[index], { selected: isSelected }), // create a new object instance with updated selected property
          ...data.slice(index + 1) // take everything after the selected index
        ];
    
        // update the listview datasource with the new data
        this.setState({
          filterDataSource: this.state.filterDataSource.cloneWithRows(this._filterData)
        });
      }
    
      renderRow (item, sectionId, rowId) {
        return(
          <View>
            <Text>{item.description}</Text>
            <Switch
              onValueChange={(value) => this.switchChanged(rowId, value)}
              value={item.selected}
            />
          </View>
        );
      }
    
      // just a test function used to dump the current state of the _filterData
      // to the console
      onPress () {
        console.log('data', this._filterData);
      }
    
      render () {
        return (
          <Modal>
            <ListView
              renderRow={this.renderRow}
              dataSource={this.state.filterDataSource}
            />
    
            <TouchableOpacity onPress={this.onPress}>
              <Text>Get Filter Data</Text>
            </TouchableOpacity>
          </Modal>
        );
      }
    }
    

    请注意,这个PopoverFilter 类还呈现一个按钮,按下该按钮时会将数据的当前状态转储到控制台,以便您查看它的当前表单。

    这是一个如何使用组件的示例:

    import React from 'react';
    import {
      AppRegistry,
      View
    } from 'react-native';
    
    import filterData from './filter';
    import PopoverFilter from './PopoverFilter';
    
    class MyApp extends React.Component {
      render () {
        return (
          <View>
            <PopoverFilter filterData={filterData.filter.track} />
          </View>
        );
      }
    }
    
    AppRegistry.registerComponent('MyApp', () => MyApp);
    

    【讨论】:

    • 嗨,梅森,首先非常感谢您的全面回复。我会尝试测试你的解决方案,它看起来很不错。就我实施您的解决方案而言,我会给您反馈。谢谢你:)
    • 嗨梅森,所以我有时间测试你的解决方案,它非常适合我想做的事情。非常感谢您的详细建议和您花费的时间。还要特别感谢有关我可以在我的代码中做得更好的更多信息:)
    • 太棒了乔纳森,我很高兴代码示例和提示对您有所帮助。如果您对此结果感到满意,请务必将其标记为答案,以便其他搜索相同问题的人可以从该解决方案中受益。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-11
    • 2018-03-01
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-12
    相关资源
    最近更新 更多