現在のところ、ユーザーが写真のコレクションをスワイプできるカスタムスワイパーコンポーネントの画面を持つReact Nativeアプリで作業しています。最初に私は10枚の写真を読み込むAPI呼び出しを行い、現在の写真インデックスが格納されている配列の終わりに近づくとスワイプして別の10を読み込みます。React Native setState(...):既存の状態遷移中に更新できません
ページ作成を行っているので、ユーザーがどのページを表示しているかを記録します。たとえば、インデックスが0〜9の場合、ユーザーは最初のページ、10〜19は2番目のページなどです。
ユーザーがいるページを正常に追跡できましたしかし、状態の中でそれを更新するときに私は警告を生成しているので、これを処理するより良い方法があると私は思う。ここで
Warning: setState(...): Cannot update during an existing state transition
(such as within `render` or another component's constructor). Render
methods should be a pure function of props and state; constructor side
effects are an anti-pattern, but can be moved to `componentWillMount`.
は、画面の私の実装です:
'use strict'
import React, { Component } from 'react'
import { Text, View, Image, Dimensions, Platform } from 'react-native'
import { StackNavigator } from 'react-navigation'
import Swiper from 'react-native-swiper'
import styles from './styles/ImageScreenStyle'
const { width, height } = Dimensions.get('window')
class ImageScreen extends Component {
constructor(props) {
super(props)
this.state = {
page: this.props.navigation.state.params.page,
key: this.props.navigation.state.params.key,
items: this.props.navigation.state.params.array,
}
this._fetchNextPage = this._fetchNextPage.bind(this)
this._renderNewItems = this._renderNewItems.bind(this)
this._renderNewPage = this._renderNewPage.bind(this)
}
// Update the parent state to push in new items
_renderNewItems(index, items) {
let oldItems = this.state.items
let newItems = oldItems.concat(items)
this.setState({ items: newItems, key: index })
}
// This generates a warning but still works?
_renderNewPage(page) {
let newPage = this.state.page
newPage.current = page
this.setState({ page: newPage })
}
render() {
return (
<Swiper
showsButtons
loop = { false }
index = { this.state.key }
renderPagination = { this._renderPagination }
renderNewItems = { this._renderNewItems }
renderNewPage = { this._renderNewPage }
fetchNextPage = { this._fetchNextPage }
page = { this.state.page }>
{ this.state.items.map((item, key) => {
return (
<View key = { key } style = { styles.slide }>
<Image
style = {{ width, height }}
resizeMode = 'contain'
source = {{ uri: item.photo.images[1].url }}
/>
</View>
)
})}
</Swiper>
)
}
_renderPagination(index, total, context) {
const photoPage = Math.floor(index/10) + 1
const currentPage = this.page.current
// Update the current page user is on
if (photoPage !== currentPage) {
return this.renderNewPage(photoPage)
}
// Add more photos when index is greater or equal than second last item
if (index >= (total - 3)) {
this.fetchNextPage().then((data) => {
// Here is where we will update the state
const photos = data.photos
let items = Array.apply(null, Array(photos.length)).map((v, i) => {
return { id: i, photo: photos[i] }
})
// Pass in the index because we want to retain our location
return this.renderNewItems(index, items)
})
}
}
_fetchNextPage() {
return new Promise((resolve, reject) => {
const currentPage = this.state.page.current
const nextPage = currentPage + 1
const totalPages = this.state.page.total
if (nextPage < totalPages) {
const PAGE_URL = '&page=' + nextPage
fetch(COLLECTION_URL + PAGE_URL + CONSUMER_KEY)
.then((response) => {
return response.json()
})
.then((data) => {
return resolve(data)
})
.catch((error) => {
return reject(error)
})
}
})
}
}
export default ImageScreen
改ページは一つの関数内で処理され、そして私は新しい写真やページの状態を処理するために_renderNewItemsと_renderNewPageメソッドを使用していますインデックス。
更新
私は、提供の回答を反映するように私のコードを変更しましたが、警告を抑制するためになって任意の運を持っていないです。私は束縛を考え出し、componentWillMount()
メソッドに変更すると助けになります。問題を修正しました
class ImageScreen extends Component {
constructor(props) {
super(props)
this.state = {
page: '',
key: '',
items: []
}
this._fetchNextPage = this._fetchNextPage.bind(this)
this._renderNewItems = this._renderNewItems.bind(this)
this._renderNewPage = this._renderNewPage.bind(this)
}
componentWillMount() {
this.setState({
page: this.props.navigation.state.params.page,
key: this.props.navigation.state.params.key,
items: this.props.navigation.state.params.array
})
}
render() {
return (
<Swiper
showsButtons
loop = { false }
index = { this.state.key }
renderPagination = { this._renderPagination.bind(this) }
renderNewItems = { this._renderNewItems.bind(this) }
renderNewPage = { this._renderNewPage.bind(this) }
fetchNextPage = { this._fetchNextPage.bind(this) }>
{ this.state.items.map((item, key) => {
return (
<View key = { key } style = { styles.slide }>
<Image
style = {{ width, height }}
resizeMode = 'contain'
source = {{ uri: item.photo.images[1].url }}
/>
</View>
)
})}
</Swiper>
)
}
_renderPagination(index, total, context) {
const photoPage = Math.floor(index/10) + 1
const statePage = this.state.page.current
if (photoPage !== statePage) {
return this._renderNewPage(photoPage)
}
if (index >= (total - 3)) {
this._fetchNextPage().then((data) => {
const photos = data.photos
let items = Array.apply(null, Array(photos.length)).map((v, i) => {
return { id: i, photo: photos[i] }
})
return this._renderNewItems(index, items)
})
}
}
_renderNewItems(index, items) {
let oldItems = this.state.items
let newItems = oldItems.concat(items)
this.setState({ items: newItems, key: index })
}
// TO-DO: Fix the warning this generates
_renderNewPage(page) {
let newPage = this.state.page
newPage.current = page
this.setState({ page: newPage })
}
_fetchNextPage() {
return new Promise((resolve, reject) => {
const currentPage = this.state.page.current
const nextPage = currentPage + 1
const totalPages = this.state.page.total
if (nextPage < totalPages) {
const PAGE_URL = '&page=' + nextPage
fetch(COLLECTION_URL + PAGE_URL + CONSUMER_KEY)
.then((response) => {
return response.json()
})
.then((data) => {
return resolve(data)
})
.catch((error) => {
return reject(error)
})
}
})
}
}
export default ImageScreen
アップデート2
:私は現在立つのはここです。フェリックスがコメントで指摘したように、renderPagination
メソッドが頻繁に再レンダリングされていたので、スワイパーのonMomentumScrollEnd
小道具(反応ネイティブswiperから)を使用してページ情報を更新しました。それを必要とするかもしれない人のために、ここに私のコードは次のとおりです。上記の場合
class ImageScreen extends Component {
constructor(props) {
super(props)
this.state = {
page: '',
key: '',
items: []
}
}
componentWillMount() {
this.setState({
page: this.props.navigation.state.params.page,
key: this.props.navigation.state.params.key,
items: this.props.navigation.state.params.array
})
}
render() {
return (
<Swiper
showsButtons
loop = { false }
index = { this.state.key }
onMomentumScrollEnd = { this._onMomentumScrollEnd.bind(this) }
renderPagination = { this._renderPagination.bind(this) }
renderNewItems = { this._renderNewItems.bind(this) }
fetchNextPage = { this._fetchNextPage.bind(this) }>
{ this.state.items.map((item, key) => {
return (
<View key = { key } style = { styles.slide }>
<Image
style = {{ width, height }}
resizeMode = 'contain'
source = {{ uri: item.photo.images[1].url }}
/>
</View>
)
})}
</Swiper>
)
}
_renderNewItems(index, items) {
let oldItems = this.state.items
let newItems = oldItems.concat(items)
this.setState({ items: newItems, key: index })
}
_renderPagination(index, total, context) {
if (index >= (total - 3)) {
this._fetchNextPage().then((data) => {
const photos = data.photos
let items = Array.apply(null, Array(photos.length)).map((v, i) => {
return { id: i, photo: photos[i] }
})
return this._renderNewItems(index, items)
})
}
}
_fetchNextPage() {
return new Promise((resolve, reject) => {
const currentPage = this.state.page.current
const nextPage = currentPage + 1
const totalPages = this.state.page.total
if (nextPage < totalPages) {
const PAGE_URL = '&page=' + nextPage
fetch(COLLECTION_URL + PAGE_URL + CONSUMER_KEY)
.then((response) => {
return response.json()
})
.then((data) => {
return resolve(data)
})
.catch((error) => {
return reject(error)
})
}
})
}
_onMomentumScrollEnd(e, state, context) {
const photoPage = Math.floor(state.index/10) + 1
const statePage = this.state.page.current
console.log('Current page: ' + photoPage)
console.log('State page: ' + statePage)
if (photoPage !== statePage) {
this._renderNewPage(photoPage)
}
}
_renderNewPage(page) {
let newPage = this.state.page
newPage.current = page
this.setState({ page: newPage })
}
}
export default ImageScreen
エラーメッセージがかなり明確と思われます私に: 'render()'が呼び出されたときに 'this.setState'を呼び出すヘルパー関数の1つを呼び出さなければなりません。それは良くありません。レンダリング時に呼び出される関数で 'this.setState'を呼び出さないでください。 –
@FelixKling私は、これが起こっている可能性がある場所を困惑しています。私の更新されたコードをもう一度見てもらえますか? –
あなたは 'Swiper'の実装を見る必要があります。 'renderNewItems'、' renderNewPage'、 'renderPagination'のような関数が' Swiper'の 'render'メソッドの中で呼び出された場合、それらのメソッドでは' setState'を呼び出すことはできません。プロペラ名が 'render'で始まるという事実は、対応するメソッドで' setState'を呼び出すべきでないことを示しているようです。あなたが望むのはレンダリングされるページを知ることだけだとすれば、 'Swiper'はコールバックを渡してこれらの変更を通知する手段を提供すると確信しています。 –