Как обновить один элемент в 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
, в основном, шаги:
напишите пользовательский компонент для рендеринга каждого пункт в "FlatList"
переопределить функцию "componentWillReceiveProps"
переопределите функцию "shouldComponentUpdate", чтобы сообщить компоненту, когда обновлять
обработайте функцию изменения в Родительском компоненте и передайте вниз каждому ребенку