Фильтрация компонентов в React Native

Я пытаюсь создать список флажков фильтрации, чтобы отфильтровать новости для отображения в ленте. Мое приложение еще не получает информацию из базы данных, поэтому я в основном создаю вручную, чтобы проверить методы.

Переменные, которые у меня есть, — это темы (которые являются свойством состояния), а feedNews, темы — это массив с темами, которые пользователь хочет видеть в ленте, а feedNews — это все компоненты новостей, которые существуют для отображения в ленте.

например тем

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

например компонентов для фидаНовости

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,
    });
}

Мой вопрос в том, что я делаю неправильно в этих функциях, или это «нормально», чтобы это происходило в реактивном родном

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

В случае, если я хочу сохранить свой первоначальный подход к прокрутке, мое решение было в основном таким

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 в основном превращается в массив объектов, как в приведенном выше примере для предыдущего решения, а затем преобразуется с помощью функции filterFeedNews с помощью фильтра и карты "преобразовать" в массив компонентов. В некотором смысле результат точно такой же, как у ListView, исходная анимация не «работает», но это из-за того, как реализована реализация.

Решение для анимации

У меня была проблема, которая вызывала все проблемы, о которых я говорил выше, на самом деле из-за LayoutAnimation: каждый раз, когда я «удалял» новостную ленту с параметрами фильтрации, Layout Animation удаляла больше, чем новостную ленту из определенной категории.

Я извиняюсь за это, так как я думал, что проблема не в LayoutAnimation, и поэтому я не опубликовал эту часть кода.

В основном для этой проблемы удаления не возникает, на данный момент решение состоит в том, чтобы сделать это:

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

Если вы спрашиваете, почему я не поставил удаление, то это потому, что в LayouAnimation удаление не работает на Android, только на iOS.

Еще раз извините за потраченное время, не выложив всю информацию


person Luís Mestre    schedule 11.04.2017    source источник


Ответы (1)


Если я не ошибаюсь, setState не сразу обновляет состояние в реакции. Поэтому, чтобы предотвратить это, я предлагаю вам вызвать функцию обратного вызова, например:

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

Имеет ли это смысл?

person Arda Ilgaz    schedule 11.04.2017
comment
Также быстрый совет, лично мне нравится это использование: 'this.setState({newsTopics});' как чисто :) - person Arda Ilgaz; 11.04.2017
comment
Это правильно, setState принимает необязательный обратный вызов в качестве второго параметра. Согласно документам: setState() не сразу изменяет this.state, а создает отложенный переход состояния. Доступ к this.state после вызова этого метода потенциально может вернуть существующее значение. Нет гарантии синхронной работы вызовов setState, и вызовы могут быть объединены в пакеты для повышения производительности. Это описано в документации здесь: facebook.github.io/react/ docs/react-component.html#setstate - person QMFNP; 11.04.2017
comment
Хотя это правильный способ сделать это, и я согласен, но, к сожалению, проблема остается даже после этого, когда он фильтрует и отображает - person Luís Mestre; 11.04.2017