我遇到了一个问题,即基于AsyncStorage中的相同键,我在多个组件中设置了setState。由于状态是在componentDidMount中设置的,并且这些组件不必卸载并安装在导航上,因此状态值和AsyncStorage值可能不同步。
这是我能做的最简单的例子。
只需设置导航和应用。
var React = require('react-native');
var B = require('./B');
var {
AppRegistry,
Navigator
} = React;
var A = React.createClass({
render() {
return (
<Navigator
initialRoute={{
component: B
}}
renderScene={(route, navigator) => {
return <route.component navigator={navigator} />;
}} />
);
}
});
AppRegistry.registerComponent('A', () => A);
B在mount上从AsyncStorage读取,然后设置为state。
var React = require('react-native');
var C = require('./C');
var {
AsyncStorage,
View,
Text,
TouchableHighlight
} = React;
var B = React.createClass({
componentDidMount() {
AsyncStorage.getItem('some-identifier').then(value => {
this.setState({
isPresent: value !== null
});
});
},
getInitialState() {
return {
isPresent: false
};
},
goToC() {
this.props.navigator.push({
component: C
});
},
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>
{this.state.isPresent
? 'Value is present'
: 'Value is not present'}
</Text>
<TouchableHighlight onPress={this.goToC}>
<Text>Click to go to C</Text>
</TouchableHighlight>
</View>
);
}
});
module.exports = B;
C从AsyncStorage读取与B相同的值,但允许您更改该值。更改切换状态和AsyncStorage中的值。
var React = require('react-native');
var {
AsyncStorage,
View,
Text,
TouchableHighlight
} = React;
var C = React.createClass({
componentDidMount() {
AsyncStorage.getItem('some-identifier').then(value => {
this.setState({
isPresent: value !== null
});
});
},
getInitialState() {
return {
isPresent: false
};
},
toggle() {
if (this.state.isPresent) {
AsyncStorage.removeItem('some-identifier').then(() => {
this.setState({
isPresent: false
});
})
} else {
AsyncStorage.setItem('some-identifier', 'some-value').then(() => {
this.setState({
isPresent: true
});
});
}
},
goToB() {
this.props.navigator.pop();
},
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>
{this.state.isPresent
? 'Value is present'
: 'Value is not present'}
</Text>
<TouchableHighlight onPress={this.toggle}>
<Text>Click to toggle</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.goToB}>
<Text>Click to go back</Text>
</TouchableHighlight>
</View>
);
}
});
module.exports = C;
如果在C中切换然后返回到B,则B中的状态和AsyncStorage中的值现在不同步。据我所知,navigator.pop()不会触发我可以用来告诉B刷新值的任何组件生命周期函数。
我所知道的一个解决方案,但不是理想的,是让B的状态成为C的支柱,并给C一个回调道具来切换它。如果B和C总是直接是父和子,那么这将很有效,但在真实应用中,导航层次结构可能更深。
无论如何在导航事件之后触发组件上的功能,或者我缺少的其他东西?
答案 0 :(得分:2)
和你一样陷入同样的问题。我希望这会改变,或者我们在组件上获得额外的事件来监听(componentDidRenderFromRoute)或类似的东西。无论如何,我如何解决它是将我的父组件保留在范围内,因此子导航栏可以调用组件上的方法。我使用的是https://github.com/Kureev/react-native-navbar,但它只是Navigator的一个包装器:
class ProjectList extends Component {
_onChange(project) {
this.props.navigator.push({
component: Project,
props: { project: project },
navigationBar: (<NavigationBar
title={project.title}
titleColor="#000000"
style={appStyles.navigator}
customPrev={<CustomPrev onPress={() => {
this.props.navigator.pop();
this._sub();
}} />}
/>)
});
}
}
我正在使用道具数据推送项目组件,并附加了我的导航栏组件。 customPrev是react-native-navbar将替换为其默认值的内容。所以在新闻中,我调用pop,并在我的ProjectList实例上调用_sub方法。
答案 1 :(得分:1)
我认为解决方案应该是AsyncStorage的包装,并可能使用“flux”架构。 https://github.com/facebook/flux在Dispatcher和Event Emitters的帮助下 - 与flux chat示例非常相似:https://github.com/facebook/flux/tree/master/examples/flux-chat
首先也是最重要的。如AsyncStorage文档中所述:https://facebook.github.io/react-native/docs/asyncstorage.html
建议您在AsyncStorage之上使用抽象 而不是AsyncStorage直接用于除了灯光使用之外的任何东西 因为它在全球范围内运作。
所以我相信您应该构建自己的特定“域”存储,它将包装通用的AsyncStorage并执行以下操作(请参阅聊天示例中的存储):
这可能看起来像是一种矫枉过正,但它解决了许多问题(包括级联更新等等 - 当应用程序变大时会很明显 - 并且它引入了一些似乎有意义的合理抽象。你可以可能只是在没有引入调度员的情况下只有1-4点 - 应该仍然可以工作但稍后可以导致Facebook描述的问题(阅读flux文档了解更多细节)。
答案 2 :(得分:1)
1)这里的主要问题在于您的体系结构 - 您需要在AsyncStorage周围创建一些包装器,当某些值发生更改时也会生成事件,类接口的示例是:
return 0
在componentWillMount中:
class Storage extends EventEmitter {
get(key) { ... }
set(key, value) { ... }
}
在componentWillUnmount中:
storage.on('someValueChanged', this.onChanged);
2)架构问题也可以通过使用storage.removeListener('someValueChanged', this.onChanged);
+ redux
,使用它的全局应用状态以及更改时自动重新呈现来修复。
3)其他方式(不是基于事件,因此不完美)是添加自定义生命周期方法,如componentDidAppear和componentDidDissapear。以下是示例react-redux
类:
BaseComponent
所以只需从该组件扩展并覆盖componentDidAppear方法(不要忘记OOP并调用内部的超级实现:import React, { Component } from 'react';
export default class BaseComponent extends Component {
constructor(props) {
super(props);
this.appeared = false;
}
componentWillMount() {
this.route = this.props.navigator.navigationContext.currentRoute;
console.log('componentWillMount', this.route.name);
this.didFocusSubscription = this.props.navigator.navigationContext.addListener('didfocus', event => {
if (this.route === event.data.route) {
this.appeared = true;
this.componentDidAppear();
} else if (this.appeared) {
this.appeared = false;
this.componentDidDisappear();
}
});
}
componentDidMount() {
console.log('componentDidMount', this.route.name);
}
componentWillUnmount() {
console.log('componentWillUnmount', this.route.name);
this.didFocusSubscription.remove();
this.componentDidDisappear();
}
componentDidAppear() {
console.log('componentDidAppear', this.route.name);
}
componentDidDisappear() {
console.log('componentDidDisappear', this.route.name);
}
}
)。
答案 3 :(得分:0)
如果您使用NavigatorIOS,则它可以将基础导航器传递给每个子组件。这有几个可以在这些组件中使用的事件:
onDidFocus功能
将使用每个场景的新路线进行调用 转换完成后或初始安装后
onItemRef功能
将调用(ref,indexInStack,route) 当场景参考改变时
onWillFocus功能
安装时会发出目标路线 每次导航过渡之前
https://facebook.github.io/react-native/docs/navigator.html#content
答案 4 :(得分:0)
我的解决方案是尝试为组件添加自定义生命周期。
this._navigator.addListener('didfocus', (event) => {
let component = event.target.currentRoute.scene;
if (component.getWrappedInstance !== undefined) {
// If component is wrapped by react-redux
component = component.getWrappedInstance();
}
if (component.componentDidFocusByNavigator !== undefined &&
typeof(component.componentDidFocusByNavigator) === 'function') {
component.componentDidFocusByNavigator();
}
});
然后,您可以在组件中添加componentDidFocusByNavigator()
来执行某些操作。