【问题标题】:React Native, render function not rendering, although the trace indicates that passes through itReact Native,渲染函数不渲染,虽然跟踪表明通过它
【发布时间】: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中的passPropsNaviagator中传递的。

一切正常,动作被正确调度,你可以在跟踪上看到,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,带有 ProviderNavigator

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


【解决方案1】:

注意:这只是作者提供的上述解决方案的复制/粘贴。

来自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} insted of {...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}
       })
    }}>

这就是所有人!

谢谢

【讨论】:

    猜你喜欢
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多