Ответ 1
Не поймите меня неправильно, ответ @ShubhnikSingh помог, но я отказался от него, потому что давно нашел лучшее решение этого вопроса и, наконец, не забыл опубликовать его здесь.
Предположим, что моя запись содержит следующие свойства:
{
postId: "-L84e-aHwBedm1FHhcqv",
date: 1525566855,
message: "My Post",
uid: "52YgRFw4jWhYL5ulK11slBv7e583",
liked: false,
likeCount: 0,
commentCount: 0
}
Где liked
показывает, понравился ли этот пост пользователю, просматривающему это сообщение, что определяет цвет кнопки "Мне нравится" (по умолчанию она серая, но красная, если liked == true
)
Вот шаги для воссоздания моего решения: сделайте "Post" a Component
и отобразите его в FlatList
. Вы можете использовать React PureComponent
, если у вас нет реквизита, который вы передаете в Post
, например, массив или объект, которые могут быть обманчиво не равны. Если вы не знаете, что это значит, просто используйте обычный Component
и переопределите shouldComponentUpdate
, как мы делаем ниже.
class Post extends Component {
// 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
// This assumes that, unlike Twitter, updates do not come from other
// instances of the application in real time.
shouldComponentUpdate(nextProps, nextState) {
const { liked, likeCount } = nextProps
const { liked: oldLiked, likeCount: oldLikeCount } = this.props
// If "liked" or "likeCount" is different, then update
return liked !== oldLiked || likeCount !== oldLikeCount
}
render() {
return (
<View>
{/* ...render other properties */}
<TouchableOpacity
onPress={() => this.props.onPressLike(this.props.postId)}
>
<Icon name="heart" color={this.props.liked ? 'gray' : 'red'} />
</TouchableOpacity>
</View>
)
}
}
Затем создайте компонент PostList
, который будет отвечать за обработку логики загрузки сообщений и обработку подобных взаимодействий:
class PostList extends Component {
/**
* As you can see, we are not storing "posts" as an array. Instead,
* we make it a JSON object. This allows us to access a post more concisely
* than if we stores posts as an array. For example:
*
* this.state.posts as an array
* findPost(postId) {
* return this.state.posts.find(post => post.id === postId)
* }
* findPost(postId) {
* return this.state.posts[postId]
* }
* 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 wants an array for its data property rather than an object,
* so we need to pass data={Object.values(this.state.posts)} rather than
* just data={this.state.posts} as one might expect.
*/
state = {
posts: {}
// Other states
}
renderItem = ({ item }) => {
const { date, message, uid, postId, other, props, here } = item
return (
<Post
date={date}
message={message}
uid={uid}
onPressLike={this.handleLikePost}
/>
)
}
handleLikePost = 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() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={Object.values(this.state.posts)}
renderItem={this.renderItem}
keyExtractor={({ item }) => item.postId}
/>
</View>
)
}
}
В итоге:
1) Написать собственный компонент (Post
) для отображения каждого элемента в "FlatList"
2) Переопределите функцию shouldComponentUpdate пользовательского компонента (Post
), чтобы сообщить компоненту, когда обновлять
Обработайте "состояние лайков" в родительском компоненте (PostList
) и передайте данные каждому дочернему элементу