如何在不丢失状态/重新创建组件的情况下呈现实例列表?

时间:2017-12-05 18:37:29

标签: reactjs react-native

尝试使用一些动画制作我自己的React路由器。打砖墙。

我正在渲染一叠屏幕 可以弹出或推送堆栈。

我的问题是,当堆栈发生更改时,状态将丢失,并且再次调用构造函数会破坏先前的状态(使堆栈无效)。 我该怎么做?

创建屏幕(在此之后我们推送到处于状态的堆栈)

/**
 * 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

屏幕类型作为唯一子项传递。

1 个答案:

答案 0 :(得分:2)

卸载组件后,其状态将永远消失。你可能会认为&#34;我有一个对组件的变量引用,所以即使它已卸载它仍然保持其状态,对吧?&#34;不,React并没有这样做。卸载组件无异于破坏它。即使你重新安装&#34;相同的&#34;组件,就React而言,它是一个全新的组件,带有全新的构造函数调用,安装生命周期等。因此,您需要放弃将React组件本身保留在数组中作为历史堆栈的方法。

令人沮丧,我知道。相信我,我遇到了同样的问题。

解决方案是从组件本身中提取视图/屏幕状态并将其提升为父级。实质上,您将状态保存在父级中的数组中,然后将它们作为道具传递到视图/屏幕本身。这可能似乎就像一个反直觉的,&#34;非反响的&#34;做事的方式,但it actually is in line with how React is intended to be used。国家通常应该被提升#34;到所有组件需要访问它的最近共同祖先的级别。在这种情况下,您需要在以上级别的视图/屏幕上访问您的状态,因此您需要将其抬起。

这里有一些假代码来说明。

现在,您的应用似乎是这样的结构:

// 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道具。多数民众赞成,这样你就可以触发对渲染屏幕的改变&#34;状态&#34; (在屏幕内实际跟踪)。