Reactjs如何在商店更新后更新组件

时间:2018-06-21 18:08:19

标签: reactjs react-native redux react-redux

我在我的本机应用程序Switcher组件中使用nachos-ui

在我的configScreen.js中,如下所示:

<Switcher
     onChange={((difficulty) => this.onLevelSelect(difficulty) )}
     defaultSelected={this.props.difficulty} >
     <TabButton value='medium' text='medium'  />
     <TabButton value='easy' text='easy'/>
 </Switcher>

,我有一个重置按钮,可以从商店重置this.props.difficulty的值。

一切正常,商店会重置,并且值恢复正常,但切换器始终指示旧值,除非我移至另一个屏幕,然后转到configScreen.js,这时更改生效(即:defaultSelected的值)表示商店的价值。

有人可以指导我如何使用componentShouldUpdate或 componentWillRecieveProps ??

action / index.js:

exports.changeLevel = (select) => {
      return {
            type: 'CHANGE_LEVEL',
            select
      }  

}

exports.resetGame = () => {
      return{
            type: 'RESET_GAME',
      }
}

reducer / index.js:

module.exports = ( state = {}, action ) => {
    switch(action.type) {
        case 'RESET_GAME' : 

            return {
                ...state,
                difficulty: 'easy',
            }
        case 'CHANGE_LEVEL':
            return {
                ...state,
                difficulty: action.select
            }
        default:
            return state
    }
}

store / index.js:

import { createStore, compose } from 'redux';
import { AsyncStorage } from 'react-native';
import { persistStore, autoRehydrate } from 'redux-persist'; 

import reducer from '../reducer';

const defaultState = {
    difficulty: 'easy',

}


export const configureStore = (intialState = defaultState, action) =>{

    var store = createStore(reducer, intialState,compose(
        autoRehydrate()
    ));
    persistStore(store, {storage: AsyncStorage});

    return store;

}

configScreen.js:

import React, { Component } from 'react';
import {connect} from 'react-redux';
import { View, Text, StyleSheet, ImageBackground, TouchableOpacity, Dimensions } from 'react-native';
import { Container, Header, Content, Card, CardItem,  Button, Icon, Left, Body } from 'native-base';
import { AdMobBanner } from "expo";
import DropdownAlert from 'react-native-dropdownalert';
import {changeLevel, reset, resetGame} from '../actions';
import {Switcher, TabButton, themeManager} from 'nachos-ui';
import { configureStore } from '../store';

const buttonTheme = themeManager.getStyle('TabButton');

const newButtonTheme = {
  ...buttonTheme,
  BUTTON_BACKGROUND:'#fff',
  BUTTON_BORDER_WIDTH:1,
  BUTTON_BORDER_COLOR:'#4bb29e',
  BUTTON_BORDER_RADIUS:5,
  BUTTON_HEIGHT:25,
  BUTTON_WIDTH: 10,
  BUTTON_FONT_COLOR:'black',
  BUTTON_FONT_SIZE:14,
  BUTTON_FONT_WEIGHT:'normal',
  BUTTON_FONT_FAMILY:'jf',
  BUTTON_SELECTED_BACKGROUND:'#4bb29e',
  BUTTON_SELECTED_FONT_COLOR:'white',
  BUTTON_SELECTED_BORDER_COLOR:'#4bb29e',

}
themeManager.setSource('TabButton', () => (newButtonTheme))

const screenWidth = Dimensions.get('window').width / 12;


class AboutScreen extends Component {

  _goBack = () => {
    this.props.navigation.goBack(null)
  }

  onClose(data) {
    // data = {type, title, message, action}
    // action means how the alert was closed.
    // returns: automatic, programmatic, tap, pan or cancel
  }

  // RESET THE STORE HERE 
  onReset() { 
    this.gameReset() 
    console.log(configureStore().getState())
  }

  onLevelSelect(selected) {

    this.props.dispatch(changeLevel(selected))

  }

  gameReset() {
    this.props.dispatch(resetGame())
  }    

  render() {
    return (
      <Container style={{backgroundColor: 'white',}}>
          <Content>
            <Card>
            <CardItem>
                          <Switcher
                            onChange={((difficulty) => 
                             this.onLevelSelect(difficulty) )}
                            defaultSelected={this.props.difficulty}
                            >
                                <TabButton value='medium' text='medium'  />
                                <TabButton value='easy' text='easy'/>
                          </Switcher>
                     <CardItem>
                        <Button title='reset' onPress={() => this.gameReset()} />
            </Card>
          </Content>

      </Container>
    );
  }
}


function mapStateToProps(state) {
  return {
    difficulty: state.difficulty,
  }
}

function mapDispatchToProps(dispatch) {
  return {
      changeLevel: () => dispatch(changeLevel(this.props.difficulty))
  }
}

export default connect(mapStateToProps)(AboutScreen)

2 个答案:

答案 0 :(得分:1)

您不应该使用componentWillRecieveProps,因为它将很快被弃用。
会被getDerivedStateFromProps (注意,与componentWillRecieveProps的工作方式不同)代替。

此外,如果正确使用它,则不必使用这些生命周期方法,因为

  1. A Prop change will trigger a rerender
  2. componentWillRecieveProps(nextProps)'s的目的是/曾经是update state on props change

如果您需要更多帮助,请发布更多代码:)

更新

一些注意事项:

componentWillUpdate(nextProps, nextState) {
    if(nextProps.difficulty !== this.props.difficulty ){
        this.setState({difficulty: this.props.difficulty})
    }
}

您要在这里实现什么?在这里执行this.setState({...});是非常危险的。因为您可以trigger an endless loop of rerenders。 同样,您什么也不做,只是将组件的本地状态分配给旧的道具。而且您没有在应用程序中的任何地方使用this.state.difficulity。实际上,您只是触发了另一个重新渲染(无论如何都会发生)

还:

function mapDispatchToProps(dispatch) {
    return {
        changeLevel: () => dispatch(changeLevel(this.props.difficulty))
    }
}

您无权访问this,因为它在类上下文之外。 您需要将this.props.difficulty (尽管您正在传递选择的内容)作为分派调用的参数。您没有在此处连接resetGame(),您应该这样做。最后,尝试在分派中使用不同的命名:

function mapDispatchToProps(dispatch) {
    return {
        onDispatchChangeLevel: (difficulty) => dispatch(changeLevel(difficulty)),
        onDispatchResetGame: () => dispatch(resetGame())
    }
}

然后,当您调度changeLevel时,将其传递给this.props.difficulty并使用连接的调度而不是this.props.dispatch

onLevelSelect(selected) {
    this.props.onDispatchChangeLevel(selected)
}

gameReset() {
    this.props.onDispatchResetGame();
}

最后:

我不确定这一点:

<Switcher onChange={((difficulty) => this.onLevelSelect(difficulty) )} defaultSelected={this.props.difficulty}>

通常,您onChange传递一个事件,并且您将其视为难易度字符串easy。但是正如我所说,我不知道Switcher的工作原理。

希望这会很有用,我认为错误出在我评论的地方。否则我可以再看看:)

答案 1 :(得分:0)

问题出在nachos-ui的切换器组件上。

react-native-element buttonGroup或内置组件解决了该问题,

nachos-ui:v0.2.0-beta.1

react-native:facebook v27的分支版本 expo SDK:v28