尝试使用一些动画制作自己的反应路由器。击中砖墙。
我正在渲染一堆屏幕。
可以弹出或推动堆栈。
我的问题是,当堆栈变化时,状态丢失,构造函数被称为再次破坏先前的状态(使堆栈无用)。我该怎么做?
创建屏幕(在此之后,我们推到状态上的堆栈)
/**
* Create a new React.Element as a screen and pass props.
*/
createNewScreen = (screenName: string, props: ?any = {}): any => {
// Props is not an object.
if (typeof props !== 'object') {
Logger.error(`Passed props to screen name ${screenName} wasn't an object or undefined. Will error next screens.`);
return;
}
let propsUnlocked = JSON.parse(JSON.stringify(props));
// Add unique screen it.
const uniqueScreenKey = this.generateRandomUniqueID();
propsUnlocked.key = uniqueScreenKey;
propsUnlocked.screenId = uniqueScreenKey;
propsUnlocked.navKing = this;
propsUnlocked.screenName = screenName;
// Find the original screen to copy from.
// This just copies the 'type'
// $FlowFixMe
return React.createElement(this.findScreenNameComponent(screenName).type, propsUnlocked);
}
渲染屏幕
render() {
return ( <View
{...this.props}
onLayout={(event) => this.onLayout(event)}
pointerEvents={this.state.isAnimating ? 'none' : undefined}
>
{ this.renderStackOfScreens() }
</View>);
};
renderStackOfScreens() {
// Render screens.
return this.state.stackOfScreens
.map((eachScreen, index) => {
// Render second last screen IF animating. Basically because we have a screen animating over the top.
if (index === this.state.stackOfScreens.length - 2 && this.state.isAnimating) {
return (
<Animated.View
key={eachScreen.props.screenId + '_parent'}
style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
{ eachScreen }
</Animated.View>
);
}
// Render last screen which is animated.
if (index === this.state.stackOfScreens.length - 1) {
return (
<Animated.View
key={eachScreen.props.screenId + '_parent'}
style={this.getOffset(this.state.animatedScreenOffset)}>
{ eachScreen }
</Animated.View>
);
}
})
// Remove the undefined values.
.filter((eachScreen) => !!eachScreen);
}
可以在这里看到完整的示例https://pastebin.com/bbazipkt
屏幕类型以独特的孩子的形式传递。
一旦组件被卸下,其状态就永远消失了。您可能会认为"好吧,我对组件有可变的参考,因此即使它已卸下,它仍然保持状态,对吗?"不,反应不起作用。卸载组件是销毁它的障碍。即使您再次重新安装了"相同"组件,就React而言,它是一个全新的组件,带有全新的构造函数,安装的生命周期等。因此,您需要放弃将React组件自己保持在阵列中的方法作为历史堆栈。
令人沮丧,我知道。相信我,我遇到了同样的问题。
解决方案是从组件本身中拉出您的视图/屏幕状态,并将其提升为父母。从本质上讲,您将各州保留在父母的数组中,然后将它们作为道具本身传递到视图/屏幕上。这可能看起来像是一种违反直觉的"无反应"做事方式,但实际上与要使用反应的使用方式一致。状态通常应"提升"到所有组件都需要从中访问的最接近的共同祖先的水平。在这种情况下,您需要在上面的一个级别上访问您的状态 视图/屏幕本身,因此您需要提起它。
以下是一些伪代码。
现在,您的应用似乎是这样结构化的:
// App state
state: {
// stackOfScreens is React components.
// This won't work if you're trying to persist state!
stackOfScreens: [
<Screen />,
<Screen />,
<Screen />
]
}
// App render function
render() {
return <div>
{
this.state.stackOfScreens.map((ea, i) => {
return <View key={i} >{ea}</View>
}
}
</div>
}
相反,应该像这样:
// App state
state: {
// stackOfScreens is an array of JS objects.
// They hold your state in a place that is persistent,
// so you can modify it and render the resulting
// React components arbitrarily
stackOfScreens: [
{
name: "screen#1",
foo: "Some sort of 'screen' state",
bar: "More state,
baz: "etc."
},
{
name: "screen#2",
foo: "Some sort of 'screen' state",
bar: "More state,
baz: "etc."
},
{
name: "screen#3",
foo: "Some sort of 'screen' state",
bar: "More state,
baz: "etc."
},
]
}
// App render function
render() {
return <div>
{
this.state.stackOfScreens.map((ea, i) => {
return <View key={i} >
<Screen stateData={ea} callback={this.screenCallback} />
</View>
}
}
</div>
}
请注意,在您渲染的屏幕组件上添加了callback
Prop。多数民众赞成,您可以从屏幕内触发更改渲染的屏幕"状态"(实际上是在父母中跟踪的)。