Как обновить один элемент в FlatList в React Native?

внимание: Я отправил ответ, лично я думаю, что это лучшее решение до сих пор. Хотя это не самый высокий рейтинг ответа, но на основе результата, который я получаю, он очень эффективен.

---------------------------------------------Исходный Вопрос-------------------------------------------------------

предположим, я пишу клон Twitter, но гораздо проще. Я помещаю каждый элемент в FlatList и визуализирую их.

чтобы" нравится "сообщение, я нажимаю кнопку" Нравится "на сообщении, и кнопка" Нравится " становится красной, я нажимаю ее снова, она становится серой.

это то, что у меня есть до сих пор: я храню все загруженные сообщения в this.state, каждый пост имеет свойство под названием "понравилось", которое является логическим, указывая, понравился ли этот пользователь этот пост или нет, когда пользователь нажимает" Нравится", я иду в state.posts обновить liked свойство этого поста, а затем использовать this.setState для обновления сообщений, таких как Итак:

// 1. FlatList
<FlatList
    ...
    data={this.state.posts}
    renderItem={this.renderPost}
    ...
/> 

// 2. renderPost
renderPost({ item, index }) {
    return (
        <View style={someStyle}>
            ... // display other properties of the post
            // Then display the "like" button
            <Icon
                name='favorite'
                size={25}
                color={item.liked ? 'red' : 'gray'}
                containerStyle={someStyle}
                iconStyle={someStyle}
                onPress={() => this.onLikePost({ item, index })}
            />
            ...
        </View>
    );
}

// 3. onLikePost
likePost({ item, index }) {
    let { posts } = this.state;
    let targetPost = posts[index];

    // Flip the 'liked' property of the targetPost
    targetPost.liked = !targetPost.liked;

    // Then update targetPost in 'posts'
    posts[index] = targetPost;

    // Then reset the 'state.posts' property
    this.setState({ posts });
}

этот подход работает, однако, он слишком медленный. Цвет кнопки "Нравится" переворачивается при нажатии, но обычно это занимает около 1 секунды до изменения цвета. Я хочу, чтобы цвет перевернулся почти в то же время, когда я нажимаю на него.

Я знаю, почему это произойдет, я, вероятно, не должен использовать this.setState, потому что когда я это сделаю, то posts состояние изменилось, и все сообщения перерисовываются, но какой другой подход я могу попробовать?

3 ответов


Если вы тестируете на android, попробуйте отключить режим разработчика. Или вы нажимаете какой-то API и обновляете сообщение на сервере и обновляете кнопку like в UI, соответствующую ответу сервера? Если это так, скажите мне, я тоже столкнулся с этим, и я решил это. Также я прокомментировал вторую последнюю строку в вашем коде, которая не нужна.

// 1. FlatList
<FlatList
    ...
    data={this.state.posts}
    renderItem={this.renderPost}
    ...
/> 

// 2. renderPost
renderPost({ item, index }) {
    return (
        <View style={someStyle}>
            ... // display other properties of the post
            // Then display the "like" button
            <Icon
                name='favorite'
                size={25}
                color={item.liked ? 'red' : 'gray'}
                containerStyle={someStyle}
                iconStyle={someStyle}
                onPress={() => this.onLikePost({ item, index })}
            />
            ...
        </View>
    );
}

// 3. onLikePost
likePost({ item, index }) {
    let { posts } = this.state;
    let targetPost = posts[index];

    // Flip the 'liked' property of the targetPost
    targetPost.liked = !targetPost.liked;

    // Then update targetPost in 'posts'
    // You probably don't need the following line.
    // posts[index] = targetPost;

    // Then reset the 'state.posts' property
    this.setState({ posts });
}

вы можете установить extraData на FlatList:

<FlatList
...
    extraData={this.state}
    data={this.state.posts}
    renderItem={this.renderPost}
    ...
/> 

, когда state.posts или state.posts ' s пункт Изменить,FlatList будет повторно визуализировать.

С FlatList#дополнительные данные [extradata]:

свойство маркера для указания списка на повторную визуализацию (поскольку оно реализует PureComponent). Если какой-либо из ваших renderItem, верхний, нижний колонтитул и т. д. функции зависят от чего-либо вне опоры данных, вставьте его здесь и обработайте его неизменно.


не поймите меня неправильно, ответ @ShubhnikSingh действительно помог, но я отказался от него, потому что нашел лучшее решение этого вопроса, давно, и, наконец, я вспомнил опубликовать его здесь.

предположим, что мой элемент post содержит следующие свойства:

{
    postId: "-L84e-aHwBedm1FHhcqv",
    date: 1525566855,
    message: "My Post",
    uid: "52YgRFw4jWhYL5ulK11slBv7e583",
    liked: false,
    likeCount: 0,
    commentCount: 0
}

здесь liked представляет, понравился ли пользователю этот пост, который определит цвет кнопки" Нравится " (по умолчанию он серый, но красный, если liked == true)

затем сделайте "Post" a Component и сделать это в FlatList. (Здесь, не используйте PureComponent, потому что PureComponent не позволяет переопределить shouldComponentUpdate функция, и это важно по причине, указанной ниже)

class Post extends Component {
    constructor(props) {
        super(props)
        this.state = {
            postId: '',
            date: new Date(),
            message: '',
            uid: '',
            liked: false,
            likeCount: 0,
            commentCount: 0
        }
    }

    // Initialize the states
    componentWillMount() {
        const { postId, date, message, uid, liked,
            likeCount, commentCount } = this.props

        this.setState({ postId, date, message,
            uid, liked, likeCount, commentCount })
    }

    componentWillReceiveProps(nextProps) {
        const { postId, date, message, uid, liked,
            likeCount, commentCount } = nextProps

        this.setState({ postId, date, message,
            uid, liked, likeCount, commentCount })
    }

    // This determines whether a rendered post should get updated
    // Look at the states here, what could be changing as time goes by?
    // Only 2 properties: "liked" and "likeCount", if the person seeing
    // this post ever presses the "like" button
    shouldComponentUpdate(nextProps, nextState) {
        const { liked, likeCount } = nextState
        const { liked: oldLiked, likeCount: oldLikeCount } = this.state

        // If "liked" or "likeCount" is different, then update
        return liked !== oldLiked || likeCount !== oldLikeCount
    }

    render() {
        return(
            <View>
                ...    // render other properties
                // render the "like" button
                // "onLikePost" is a function passed down to this component
                <TouchableOpacity
                    onPress={() => this.props.onLikePost({ postId: this.state.postId })}
                >
                    <Icon
                        name='heart'
                        color={this.state.liked ? "gray" : "red"}
                    />
                </TouchableOpacity>
            </View>
        )
    }
}

затем, в другом месте, например, на экране, где вы рендерите свои сообщения, скажите MainScreen.js:

class MainScreen extends Component {
    constructor(props) {
        super(props)
        this.state = {
            posts: {}
            ... // Other states
        }

        // As you can see, we are not making "posts" an array, instead
        // we make it a json object, because, when you want to access
        // a specific post by its "postId", you won't have to iterate
        // through the whole array, you can just call "posts[postId]"
        // to access it immediately:
        // "posts": {
        //     "<post_id_1>": { "message": "", "uid": "", ... },
        //     "<post_id_2>": { "message": "", "uid": "", ... },
        //     "<post_id_3>": { "message": "", "uid": "", ... }
        // }
        // "FlatList" takes array only, therefore, you can just use "lodash":
        // _.values(this.state.posts)

        this.renderItem = this.renderItem.bind(this)
    }

    renderItem({ item }) {
        // Call the custom component you wrote here and pass all props

        const { date, message, uid, postId, ... } = item
        return (
            <Post
                date={date}
                message={message}
                uid={uid}
                ...
                onPressLike={this.onLikePost}
            />
        )
    }

    onLikePost({ postId }) {
        let post = this.state.posts[postId]
        const { liked, likeCount } = post

        const newPost = {
            ...post,
            liked: !liked,
            likeCount: liked ? likeCount - 1 : likeCount + 1
        }

        this.setState({
            posts: {
                ...this.state.posts,
                [postId]: newPost
            }
        })
    }

    render() {
        <View style={{ flex: 1 }}>
            <FlatList
                data={_.value(this.state.posts)}
                renderItem={this.renderItem}
                keyExtractor={({ item }) => item.postId}
            />
        </View>
    }
}

вот как вы обновляете один элемент в FlatList, в основном, шаги:

  1. напишите пользовательский компонент для рендеринга каждого пункт в "FlatList"

  2. переопределить функцию "componentWillReceiveProps"

  3. переопределите функцию "shouldComponentUpdate", чтобы сообщить компоненту, когда обновлять

  4. обработайте функцию изменения в Родительском компоненте и передайте вниз каждому ребенку