【react-native】参考にできないAndroidのイケてないrender制御
主旨
Android限定、レンダリングが正常に動かない現象のパワーソリューションです。
それぞれの原因は仮説ベースになっていますのでご了承ください。
備忘録と、誰かの救済策になればと思って共有します。
もし、具体的な原因、もしくはスマートな解決方法をお持ちの方は(特にカルーセル)
教えていただければ大変うれしいです!
カルーセルのレンダリングが失敗する
react-native-snap-carousel
はカルーセルコンポーネントを実現するためのライブラリです。
スワイプと、ボタンでコンポーネントの表示切り替えができるものです。
const Container = () => {
const [activeIndex, changeSlide] = React.useState(firstItem)
const carouselRef = React.useRef(null)
const onNext = React.useCallback(() => {
const lastIndex = data.length - 1
const nextIndex = lastIndex ? activeIndex : activeIndex + 1;
const carousel = carouselRef.current
// ここでカルーセルに指定した値までスナップさせる
carousel.snapToItem(activeIndex + 1)
changeSlide(nextIndex)
}, [activeIndex])
return (
<View>
<Carousel
...
ref={carouselRef}
data={data}
onSnapToItem={changeSlide}
renderItem={({item}) => <Card item={item} index={activeIndex} onNext={onNext} />}
/>
<Text style={{color: "red"}}>{`Active index: ${activeIndex}`}</Text>
</View>
)
}
スワイプでの表示には問題がないのですが・・・。
「次へ」ボタンを押すとAndroidではこんな表示になってしまいます。
「次へ」ボタンを押したときでも、スワイプと同じように次のカードがセンターに動いてほしいのですが、うまくいきません。
この事象でわかっていること
-
onPress
したときにonNext
は動く - renderは発火するしstateも変化する
- iOSでは問題ない。Androidのみ発生する
Webview × Cookie で認証が安定しない
Webview時、CookieでAuth認証を行う際にWeb側がCookieを認識しないことがありました。
使用したライブラリはreact-native-cookies
です。
スクショはないですが次のような実装を行っています。(いくつか省略しています)
// cookie.js
import CookieManager from 'react-native-cookies';
...
export const cookieSet = async (name: string, value: string) => {
const setValue = `${name}=${value}; path=/; expires=${expiration || ''}; secure`;
// AndroidではsetFromResponseを使用する
await CookieManager.setFromResponse(domain, setValue);
};
// AuthWebview.jsx
import { WebView } from 'react-native-webview';
class AuthWebview extends Component {
...
async componentDidMount() {
const { token } = this.props;
if (token) {
await cookieSet('_token', token);
}
}
...
return (
<WebView
{...this.props}
...
useWebKit
javaScriptEnabled
thirdPartyCookiesEnabled
sharedCookiesEnabled
/>
);
}
この事象でわかっていること
- 認証できるときもあれば、できないときもあるような再現性のない状態
- 1/5くらいで発生した。
- Promiseをawaitしていないとに似ていて、その方向でデバッグするが空振り
- iOSでは問題ない。Androidのみ発生する
共通してどうしたか
カルーセル
setTimeoutを使用して、snapToItem
の処理を遅らせます。
- carousel.snapToItem(activeIndex + 1)
+ setTimeout(() => carousel.snapToItem(nextIndex), 10)
(Promiseにして
snapToItem
直前にawait sleep(10)
としてもよいです)
すると、うまく表示されるようになります
snap-carouselのissueにもこの解決策は掲載されています。
Webview
すんごく気持ちわるいのですが、stateにrenderフラグを追加し、cookieセット後にrenderが実行されるように調整します。
// AuthWebview.jsx
...
async componentDidMount() {
const { token } = this.props;
if (token) {
await cookieSet('_token', token);
}
setTimeout(() => {
// Androidのためにレンダリングを遅延させる制御
this.setState({ isRender: true });
});
}
...
render() {
if (!this.state.isRender) return null;
...
}
すると、認証が100%成功するようになりました。
それぞれの仮説
カルーセル
- onNext内の
changeSlide
とcarousel.snapToItem
が同時に実行されることが原因? - つまり「stateの変更によるレンダリング」と、「snapToItemによるアニメーションの実行(レンダリング)」が共存することができず、snapToItemの処理が死んでしまう?
- この2つの処理の実行タイミングを無理やりズラしてあげることでそれぞれのレンダリングが邪魔されずに実行された?
WebviewCookie
- CookieのセットはWebviewコンポーネントに対してstateやpropsの変更を加えないのでそもそも再レンダリングがされない
- そのためcomponentDidMountでsetCookieしてもその結果がrenderに反映されるかどうかは不確定なまま
- cookieがセットされた状態でrenderを走らせる状況を無理やり作ることで、「Cookieが保存された状態でWebviewがサイトにアクセスする」状況が安定した
まとめ
これまでsetTimeout
を使って問題を解決する発想がなく、結構な時間を消費しました。。。
ここで掲載した2つの事象は、状況も要因もそれぞれ異なるものですが、解決方法としてどちらもズラすためにsetTimeout
を使ったソリューションです。
WebviewとCookieに関してはcomponentDidMount
ではないタイミングで処理したほうがいいかもしれませんが、どこで実行するのがよいのでしょうね。
具体的な原因、スマート解決方法をお持ちの方はぜひコメントください!
Author And Source
この問題について(【react-native】参考にできないAndroidのイケてないrender制御), 我々は、より多くの情報をここで見つけました https://qiita.com/nitaking/items/aea46430c3f460f0f338著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .