【发布时间】:2016-06-16 15:06:32
【问题描述】:
此问题末尾添加的解决方案,灵感来自@grabbou 的https://github.com/facebook/react-native/issues/6268
大幕拉开,第一个场景“Scene1”出现。 Scene1 是一个用来自react-redux 框架的'connect' 包装的展示组件,用于将状态和动作绑定到它们的道具。
Actions 工作得很好,并在屏幕上呈现状态,计数器。
向前点击到第二个场景'Scene2',和第一个组件一模一样,但是props(和Scene1一样)是通过renderScene中的passProps在Naviagator中传递的。
一切正常,动作被正确调度,你可以在跟踪上看到,render 函数被调用来绘制计数器,你可以再次在跟踪中看到,但是不起作用!。 Scene1 中的内部组件日志!怎么了?
这是直接进入Scene2并单击两次以增加状态后的轨迹。
这是一个 bug-Native React?
我正在使用
- “react-native”:“0.19.0”,
- “react-redux”:“4.1.2”,
- “redux”:“3.1.7”,
这就是所有的代码,如果你能帮助我的话。 没有让演示文稿风格化,因此屏幕上的结果非常简单。
1.index.ios.js
的简单代码 'use strict';
import React, {
AppRegistry,
} from 'react-native';
import App from './src/App'
AppRegistry.registerComponent('App', () => App);
2.这是App.js的代码:
'use strict';
import React, {
Navigator,
Component,
View, ListView, ScrollView,
Text, TouchableOpacity
} from 'react-native';
import { Provider, connect } from "react-redux";
import { createStore, applyMiddleware, combineReducers, bindActionCreators } from "redux";
import thunkMiddleware from "redux-thunk";
import createLogger from "redux-logger";
2.1 redux 部分
// REDUX BEGIN
//Actions
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
//Actions creators
const increment = () => ({ type: INCREMENT })
const decrement = () => ({ type: DECREMENT })
//Redux Initial State
const initialState = {
counter: 0
}
//Reducer
const reducer = (state = initialState, action = {}) => {
let delta = 1
switch (action.type) {
case DECREMENT: delta = -1;
case INCREMENT:
return Object.assign({}, state, { counter: state.counter+delta })
default:
return state
}
}
//Redux Middelware
const loggerMiddleware = createLogger();
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware,
loggerMiddleware
)(createStore);
//Wrapper to bind state and actions to props on Presentational Component
const connectComponent = (component) => connect(
(state) => ({
counter: state.counter
}),
(dispatch) => ({
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement())
})
)(component)
// REDUX END
2.2 App root,带有 Provider 和 Navigator
// APP
export default class App extends Component {
render () {
return (
<Provider store={createStoreWithMiddleware(reducer, initialState)}>
<Navigator style={{flex: 1}}
initialRoute={{
name: 'Scene1',
component: connectComponent(Scene1),
}}
renderScene={ (route, navigator) => {
const Component = route.component;
return (
<View style={{flex: 1, marginTop:40}}>
<Component navigator={navigator} route={route} {...route.passProps} />
</View>
);
}}
/>
</Provider>
)
}
}
2.3.
两个场景中用于渲染计数器的内部组件。
有一些跟踪,表明shouldComponentUpdate 被触发并返回True(你必须更新!)跟踪的时间表明在调度操作后仅几毫秒就被调用。
和其他显示达到render 功能,但不渲染在 Scene2 中。
痕迹显示,这个组件他一直以为是Scene1!!
class Counter extends Component{
constructor (props, context){
super(props, context);
}
shouldComponentUpdate(nextProps, nextState){
//Begin log
const repeat = (str, times) => (new Array(times + 1)).join(str);
const pad = (num, maxLength) => repeat(`0`, maxLength - num.toString().length) + num;
const formatTime = (time) => `@ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}`;
console.log('shouldComponentUpdate '+this.props.route.name+ ': '+ (nextProps.counter !== this.props.counter) +' '+formatTime(new Date()));
//End log
return nextProps.counter !== this.props.counter;
}
render() {
console.log('onRender: '+this.props.counter);
return (
<View>
<Text style={{fontSize: 100}}>{this.props.counter}</Text>
<TouchableOpacity onPress={()=>{this.props.increment()}} ><Text style={{fontSize: 40}}>{'<'}+{'>'}</Text></TouchableOpacity>
<TouchableOpacity onPress={()=>{this.props.decrement()}} ><Text style={{fontSize: 40}}>{'<'}-{'>'}</Text></TouchableOpacity>
<Text>----</Text>
</View>
)
}
}
2.4. 两个场景,是相等的,只是前进或后退的按钮
class Scene1 extends Component {
render() {
return (
<View>
<Text style={{fontSize: 40}}>Scene1</Text>
<Counter {...this.props}/>
<TouchableOpacity onPress={()=>{
this.props.navigator.push({
name: 'Scene2',
component: Scene2,
passProps: {...this.props}
})
}}>
<Text style={{fontSize: 20}}>{'<'}Forward{'>'} to Scene2</Text>
</TouchableOpacity>
</View>
)
}
}
class Scene2 extends Component {
render() {
return (
<View>
<Text style={{fontSize: 40}}>Scene2</Text>
<Counter {...this.props}/>
<TouchableOpacity onPress={()=>{
this.props.navigator.pop()
}} >
<Text style={{fontSize: 20}}>{'<'}Back{'>'} to Scene1</Text>
</TouchableOpacity>
</View>
)
}
}
最后一些'硬拷贝'来显示'显示'
显示计数器的 Scene2 和用于调度操作的两个按钮。 单击这些操作不会呈现计数器,但会正确分派这些操作。
只需转到 Scene2 并单击两次 以增加计数器。
Counter 组件是他的跟踪显示route.name,但它显示在 Scene1 上!这里有什么问题?
好了,戏结束了,大幕已经落下。
这是一个非常戏剧性的场景。 (只是场景2)
我想知道为什么它不起作用。
本机反应问题?
谢谢大家
解决方案
来自https://github.com/facebook/react-native/issues/6268
@grabbou 启发了这些更改,他建议将所有 App 包装为一个容器,然后将 Store 和 Actions 作为简单的道具传递给所有场景。
要进行这些更改,请创建一个新组件 RootComponent 并将应用程序连接到 Redux 存储和类似这样的操作。
export default class RootComponent extends Component {
render () {
const AppContainer = connectComponent(App); //<< App has to be container
return (
<Provider store={createStoreWithMiddleware(reducer, initialState)}>
<AppContainer/>
</Provider>
)
}
}
然后 App 更改删除 Provider 并将 Scene1 作为哑组件传递,然后 renderScene 将 {...this.props} 插入 {...route.passProps}
class App extends Component {
render () {
return (
<Navigator style={{flex: 1}}
initialRoute={{
name: 'Scene1',
component: Scene1,
}}
renderScene={ (route, navigator) => {
const Component = route.component;
return (
<View style={{flex: 1, marginTop:40}}>
<Component navigator={navigator} route={route} {...this.props} />
</View>
);
}}
/>
)
}
}
从 Scene1 中的 navigator.push 中移除 passProps,因为在 renderScene 中已经默认传递了
<TouchableOpacity onPress={()=>{
this.props.navigator.push({
name: 'Scene2',
component: Scene2,
//passProps: {...this.props}
})
}}>
这就是所有人!
谢谢
【问题讨论】:
-
太棒了,您回答了自己的问题并提供了解决方案。但是,我是否可以建议不要将其添加到问题的末尾,而是将其添加为实际答案?这样,它就会在搜索中显示为已回答。
-
感谢@Chris Geirman,在我添加新内容作为回复的其他问题中,一些审稿人告诉我,我无法添加对我的问题的回复,所以我添加了编辑问题的解决方案
-
好的,我复制/粘贴了您的解决方案作为您的解决方案。
标签: react-native render redux navigator react-redux