在 React Native 上过滤组件

Posted

技术标签:

【中文标题】在 React Native 上过滤组件【英文标题】:Filtering Components on React Native 【发布时间】:2017-09-06 22:30:10 【问题描述】:

我正在尝试制作一个过滤复选框列表选项来过滤掉要在提要上显示的新闻。我的应用还没有从数据库中获取信息,所以我基本上是手动创建来测试这些方法。

我拥有的变量是主题(这是一个状态道具),而 feedNews,topics 是一个数组,其中包含用户希望在 feed 中看到的主题,而 feedNews 是存在的所有新闻组件,可以显示在饲料。

例如主题

this.state = 
  topics: ['News-1','News-2','News-3']

例如feedNews 的组件数

const feedNews = [
    name:'News-1', comp: <FeedNews key=101 name="News-1" />,
    name:'News-2', comp: <FeedNews key=102 name="News-2" />,
    name:'News-3', comp: <FeedNews key=103 name="News-3" />,
    name:'News-1', comp: <FeedNews key=104 name="News-1" />,
    name:'News-3', comp: <FeedNews key=105 name="News-3" />
]

注意:每个组件上的键仅用于测试

基本上,我在渲染类时调用了一个函数,该函数返回过滤后的组件数组:

filterFeedNews()
    let topics = this.state.topics;
    return feedNews.filter((item) => 
        return topics.indexOf(item.name)!==-1;
    ).map(item => item.comp);

现在,每次我第一次打开应用程序时,这个功能都会起作用,但如果我真的用我的过滤器选项(复选框列表)更改主题数组,有时有些选项会消失,它们不是我的选项选择了。顺便说一句,更新过滤功能是这样的:

updateFilter(newsName, value)
    let newNewsTopics = this.state.topics;
    if(value)
        newNewsTopics.push(newsName);
    else
        newNewsTopics.splice(newNewsTopics.indexOf(newsName),1);
    
    this.props.dispatch(setNewsFilter(newNewsTopics));
    this.setState(
        newsTopics: newNewsTopics,
    );

这个函数被每个复选框内的绑定调用为动作(因为道具的名称)

onClick()
    this.action(this.name, !this.state.value);
    this.setState(
        value: !this.state.value,
    );

我的问题是,我在这些功能上做错了什么,或者在 react-native 中发生这种情况是否“正常”

PS:如果每个主题只有一条新闻,没有问题。仅当每个主题有多个新闻时,这才不起作用

更新

我不确定这是否是问题,但如果问题出在 ScrollView 而不是 ListView 用于过滤和渲染目的

render()
        return (
            <View style=styles.root>
                <Toolbar home=true options=true title='Feed' actionOptions=this.optionsAction.bind(this)/>
                <View style=styles.flexContainer>
                    <ScrollView style=styles.flexContainer>
                        this.filterFeedNews()
                    </ScrollView>
                </View>
                <View style=styles.spacing />
                this.options()
            </View>
        );
    

解决方案

所以基本上主要的甚至不是过滤,问题更多在于组件的渲染。在一位同事的帮助下,我不得不稍微改变我上面发布的内容的结构。 发生了什么变化:

const feedNews = [
    name:'News-1', ...otherProps,
    name:'News-2', ...otherProps,
    name:'News-3', ...otherProps,
    name:'News-1', ...otherProps,
    name:'News-3', ...otherProps
];

向我的状态添加了一个数据源

dataSource: ds.cloneWithRows(feedNews),

我的过滤器提要功能也进行了更改以适应新的思维方式

filterFeedNews()
        let topics = this.state.newsTopics;
        let feed = feedNews.filter((item) => 
            return topics.includes(item.name);
        );
        const ds = new ListView.DataSource(rowHasChanged: (r1, r2) => r1 !== r2);
        this.setState(dataSource: ds.cloneWithRows(feed));
    

我的更新过滤器操作也必须更改

updateFilter(newsName, value)
        let newNewsTopics = this.state.topics;
        if(value)
            newNewsTopics.push(newsName);
        else
            newNewsTopics.splice(newNewsTopics.indexOf(newsName), 1);
        
        this.props.dispatch(setNewsFilter(newNewsTopics));
        this.setState(
            newsTopics: newNewsTopics,
        , () => this.filterFeedNews());
    

我的渲染而不是 ScrollView 现在有一个 ListView

<ListView
        style=styles.flexContainer
        dataSource=this.state.dataSource
        removeClippedSubviews=false
        renderRow=(rowData, sectionID, rowID) => this.renderRow(rowData, sectionID, rowID)
        enableEmptySections=true
/>

专业人士:它没有我以前的方法遇到的问题

缺点:我每次更新组件时使用的 LayoutAnimation 不适用于 ListView,因此如果提要中的新闻有他们的图片,用户只有过滤的反馈

维护ScrollView的解决方案

如果我想保持最初的 Scroll View 方法,我的解决方案基本上是这样的

updateFilter(newsName, value)
     let newNewsTopics = this.state.topics;
     if(value)
         newNewsTopics.push(newsName);
     else
         newNewsTopics.splice(newNewsTopics.indexOf(newsName), 1);
     
     this.props.dispatch(setNewsFilter(newNewsTopics));
     this.setState(
        newsTopics: newNewsTopics,
    );


filterFeedNews()
    let topics = this.state.topics;
    let i = 0;
    return feedNews.filter((item) => 
        return topics.includes(item.name);
    ).map((item) => 
        return (<FeedNews key=++i name=item.name/>);
    );

this.state.topics 维护结构(一个字符串数组),而 feedNews 基本上变成了一个对象数组,就像上一个 solution 的例子一样,然后它被转换为函数 filterFeedNews 使用过滤器和映射“转换”为组件数组。 在某种程度上,结果与 ListView 完全一样,原来的动画没有“工作”,但这是因为实现的方式。

动画解决方案

导致上面提到的所有问题的问题实际上是由于 LayoutAnimation,每次我“删除”带有过滤选项的新闻提要时,布局动画最终从特定类别中删除的不仅仅是新闻提要。

对此我深表歉意,因为我认为 LayoutAnimation 不是问题,因此我没有发布那部分代码。

基本不会出现这种删除问题,目前解决办法是这样:

LayoutAnimation.configureNext(
        duration: 300,
        create: 
            type: LayoutAnimation.Types.easeInEaseOut,
            property: LayoutAnimation.Properties.opacity,
        ,
        update:  type: LayoutAnimation.Types.easeInEaseOut ,
    );

如果你问我为什么不删除,那是因为在 LayouAnimation 中删除在 android 上不能正常工作,只能在 ios 上工作

再次抱歉,没有提供所有信息就浪费了您的时间

【问题讨论】:

【参考方案1】:

如果我没记错的话,setState 不会立即更新 react 中的状态。所以为了防止这种情况,我建议你在回调上调用函数,如下所示:

this.setState(
    value: !this.state.value,
, ()=> 
    this.action(this.name, this.state.value);
);

这有意义吗?

【讨论】:

另外快速提示,我个人喜欢这种用法:'this.setState(newsTopics);'因为它很干净:) 这是正确的,setState 将可选回调作为第二个参数。根据文档: setState() 不会立即改变 this.state 而是创建一个挂起的状态转换。在调用此方法后访问 this.state 可能会返回现有值。无法保证对 setState 的调用的同步操作,并且可能会批处理调用以提高性能。此处的文档对此进行了介绍:facebook.github.io/react/docs/react-component.html#setstate 虽然这是正确的做法,我同意,但不幸的是,即使在此之后,当它过滤和渲染时,问题仍然存在

以上是关于在 React Native 上过滤组件的主要内容,如果未能解决你的问题,请参考以下文章

react native:TouchableOpacity 组件上的样式道具

React Native - 在图标上渲染组件

React Native:组件上的 onPress 不起作用

react-native 自定义 下拉刷新 / 上拉加载更多 组件

使用 react-native-web 实现组件

React Native - 直接在父组件上设置子组件的状态