【问题标题】:'Warning: setState(...): Cannot update during an existing state transition' after adding react-native-router-flux Action()'警告:setState(...):在现有状态转换期间无法更新'添加 react-native-router-flux Action()
【发布时间】:2017-01-23 03:19:30
【问题描述】:

我有一个新的 React Native 应用程序 - 使用 redux、reduxThunk 和 react-native-router

我有一个 listItem 组件,当用户点击它时会触发一个 redux 操作 addBrandForUser()

class ListItem extends Component {

    constructor(props) {
        super(props);
        this.onAddClick = this.onAddClick.bind(this);
    }

    onAddClick() {
        let {addBrandForUser, brand} = this.props;
        addBrandForUser({userId: null, brand: brand});
    }

    render() {
        let {brand} = this.props;

        return (
            <Text style={styles.brandSelectItem}
                  onPress={this.onAddClick}>
                {brand.title}
            </Text>
        )
    }
}

点击触发以下动作:

export const addBrandForUser = ({userId, brand}) => {
    return (dispatch) => {
        dispatch({
            type: types.AddBrandToUser,
            payload: {userId, brand}
        });

        dispatch({
            type: types.SelectBrand,
            payload: brand
        });

        //Actions.main();
    }
}

然后在所有正确的减速器中进行评估。这一切都正常工作并且正确链接 - 所有调试器都到达了我期望它们的位置。

但是,我得到了 Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount 异常 .... 但前提是 Actions.main(); 行未注释。

我的路由器配置如下:

export default class RouterComponent extends Component {
    render() {
        return (
            <Router>
                <Scene key="main" component={Main} title="Butler" hideNavBar={true} initial/>
                <Scene sceneStyle={{paddingTop: 60}} key="addBrand" component={AvailableBrands} title="Brands" hideNavBar={false}/>
            </Router>
        );
    }
}

我注意到的一件事是,在调度SelectBrand 操作后,reducer 会正确更新,并且在导航回 Actions.main() 时视图也会相应更新。但是,在调度 AddBrandToUser 操作后,reducer 获得了正确的负载并正确返回了新对象,但视图没有相应地更新。

应该更新的视图如下

class UserBrandList extends Component {

    renderBrands(brand) {
        return <BrandSelectItem brand={brand}/>;
    }

    render() {
        let {flex1} = sharedStyles;
        console.log('Render() ... Props.Brands: ');
        console.log(this.props.brands);

        return (
            <ScrollView style={flex1}>
                <ListView
                    enableEmptySections={true}
                    dataSource={this.props.brands}
                    renderRow={this.renderBrands.bind(this)}
                />
            </ScrollView>
        )
    }
}

const ds = new ListView.DataSource({
    rowHasChanged: (r1, r2) => r1 !== r2
});

const mapStateToProps = state => {
    console.log('MapStateToProps() .... state.userBrands: ');
    console.log(state.userBrands);
    return {
        brands: ds.cloneWithRows(state.userBrands)
    }
};

export default connect(mapStateToProps)(UserBrandList);

以下是上述类中的一些日志输出。此序列在 listItem 上触发 addBrandForUser() 操作时开始。

看起来新数组正在被正确缩减,但由于异常而恢复到原来的状态。

更新 1: 这是addBrandForUser() 命中的reducer。

export default (state = [], action) => {

    switch (action.type) {
        case types.RefreshUserBrands:
            return action.payload;

        case types.AddBrandToUser:
            let newArray = [...state, action.payload.brand];
            console.log('New Reduced Array: ' );
            console.log(newArray);
            return newArray;

        default:
            return state;
    }

}

更新 2: Actions 是 react-native-router-flux 提供给我的一个类:

import {Actions} from 'react-native-router-flux';
<Scene key="main" component={Main} title="Butler" hideNavBar={true} initial/>

Actions.main() 据我了解,路由到 scene 键为 main

Main路由附加的组件如下:

export default class Main extends Component {

    constructor(props) {
        super(props);
        this.openSideMenu = this.openSideMenu.bind(this);
        this.closeSideMenu = this.closeSideMenu.bind(this);
    }

    closeSideMenu() {
        this._drawer.close()
    }

    openSideMenu() {
        this._drawer.open()
    }

    render() {
        return (
            <View style={styles.container}>

                <ButlerHeader openDrawer={this.openSideMenu}/>

                <Drawer
                    type="overlay"
                    ref={(ref) =>  {this._drawer = ref }}
                    content={<SideMenu closeDrawer={this.closeSideMenu} />}
                    openDrawerOffset={0.2}
                    drawerpanCloseMask={0.2}
                    closedDrawerOffset={-3}
                    tweenHandler={(ratio) => ({
                            main: { opacity:(2-ratio)/2 }
                        })}
                />

            </View>
        );
    }
}

【问题讨论】:

  • Actions.main() 是做什么的?如果该行触发了错误,那就是您需要深入研究的那个……
  • Actions.main() 根据我在路由器组件中的配置命中路由器。我正在使用 react-native-router-flux
  • 你能展示你的减速器吗?
  • 如果你注释掉Actions.main()AddBrandToUser 会更新视图吗?
  • 无法判断,因为如果我不调用 Actions.main() 我不会路由到新场景。

标签: reactjs react-native react-redux react-native-router-flux


【解决方案1】:

想象一下顺序无关紧要……而是首先触发您的Actions.main(),然后您在尝试导航时尝试更改数据。 这很可能是正在发生的事情。

【讨论】:

    【解决方案2】:

    如果您将代码显示给Action.main,将会有所帮助。但是我猜动作创建者有点搞乱状态,或者有一些你没有提到的异步事情。

    在这种情况下可以使用的一个小技巧是将您的 Actions.main 呼叫包装在以下代码中:

    setTimeout(() =&gt; Actions.main(), 0)

    所以它在调度和状态更新完成后的下一个滴答声中运行。

    要明确一点:这不是要走的路,这种错误通常是一种很好的代码气味,应该以不同的方式完成。 ;)

    【讨论】:

    • 100% 同意这很可能是代码异味,我绝对不会尝试使用任何黑客来包扎它。我在问题的更新 2 下更新了关于 Actions.main() 的问题。
    【解决方案3】:

    虽然选择了答案。我只想发布我发现的内容。

    结论是Action.main()最终会调用setState()方法。

    也许react-native-router-flux 应该像this way 一样使用。

    首先,Action.main() 将从react-native-router-flux/blob/master/src/Actions.js L180 调用this.callback

    this[key] =
      (props = {}) => {
        assert(this.callback, 'Actions.callback is not defined!');
        this.callback({ key, type, ...filterParam(props) });
      };
    

    this.callbackreact-native-router-flux/blob/master/src/Router.js L137 中定义。

    Actions.callback = (props) => {
      const constAction = (props.type && ActionMap[props.type] ? ActionMap[props.type] : null);
      if (this.props.dispatch) {
        if (constAction) {
          this.props.dispatch({ ...props, type: constAction });
        } else {
          this.props.dispatch(props);
        }
      }
      return (constAction ? onNavigate({ ...props, type: constAction }) : onNavigate(props));
    };
    

    然后onNavigate 在 L132 中被 renderNavigation(navigationState, onNavigate) 传递。并且 renderNavigation 在 L156 中传递给 &lt;NavigationRootContainer&gt;NavigationRootContainerNavigationExperimental 导入。

    react-native-experimental-navigation/blob/master/NavigationRootContainer.js,我们可以得到onNavigate正好是handleNavigation。在那里,你可以找到setState

      handleNavigation(action: Object): boolean {
        const navState = this.props.reducer(this.state.navState, action);
        if (navState === this.state.navState) {
          return false;
        }
        this.setState({
          // $FlowFixMe - navState is missing properties :(
          navState,
        });
    
        if (this.props.persistenceKey) {
          AsyncStorage.setItem(this.props.persistenceKey, JSON.stringify(navState));
        }
    
        return true;
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-20
      • 1970-01-01
      • 2017-05-16
      • 2016-09-20
      • 2017-05-31
      • 2017-01-05
      相关资源
      最近更新 更多