【问题标题】:React Native + Redux: Why does Switch immediately turns back to false after being switched to true?React Native + Redux:为什么 Switch 切换为 true 后立即变回 false?
【发布时间】:2017-01-06 10:46:01
【问题描述】:

在 iOS React Native + Redux 中,我使用以下 Switch 组件 (https://facebook.github.io/react-native/docs/switch.html)。它首先设置为关闭,但是当打开时,它会立即自行关闭。可能是什么问题?

这是我的设置:

<Switch
  onValueChange={this._handleSwitch}
  value={switch.currentValue}
/>

而触发的动作是:

  _handleSwitch(value) {
    this.props.actions.triggerSwitch(value)
  }

动作是:

export function triggerSwitch(value) {
  return {
    type: TRIGGER_SWITCH,
    currentValue: value
  }
}

在reducer中:

const initialState = {
  currentValue: false
}

function switchReducer(switch = initialState, action) {
  switch(action.type) {
    case TRIGGER_SWITCH:
      return {
        currentValue: action.currentValue
      }

     default:
       return switch
  }
}

export default switchReducer

谢谢!

【问题讨论】:

  • 尝试检查你的默认情况,好像是在TRIGGER_SWITCH之后调用的
  • 使用_handleSwitch = (value) =&gt; { this.props.actions.triggerSwitch(value)}
  • @Maxx 难道是我的redux设置不正确?请看看这个stackoverflow.com/questions/39235637/…

标签: javascript reactjs react-native redux react-jsx


【解决方案1】:

不是因为redux,Switch要工作,我们需要从一个状态显式设置value,否则立即设置回false

<Switch
  value={this.state.hasRead}
  onValueChange={(value) => {
    this.setState({
      hasRead: value
    })
}} />

【讨论】:

    【解决方案2】:

    我尝试使用 redux 和 Switch 重现所描述的问题,但我遇到的唯一问题是 switch 是保留字,因此我将其更改为 switchState。如果有人需要工作示例:

    js/actions/actionTypes.js

    export const TRIGGER_SWITCH = 'TRIGGER_SWITCH';
    

    js/actions/switchActions.js

    import { TRIGGER_SWITCH } from './actionTypes';
    
    export const triggerSwitch = value => ({
      type: TRIGGER_SWITCH,
      currentValue: value
    });
    

    js/reducers/switchReducer.js

    import { TRIGGER_SWITCH } from '../actions/actionTypes';
    
    const initialState = {
      currentValue: false
    };
    
    const switchReducer = (state = initialState, action) => {
      switch(action.type) {
        case TRIGGER_SWITCH:
          return {
            currentValue: action.currentValue
          };
        default:
          return state;
      }
    };
    
    export default switchReducer;
    

    js/store.js

    import {
      createStore,
      applyMiddleware,
      combineReducers
    } from 'redux';
    import { createLogger } from 'redux-logger';
    import switchReducer from './reducers/switchReducer';
    
    const logger = createLogger();
    
    export default (initialState = {}) => (
      createStore(
        combineReducers({
          switchState: switchReducer
        }),
        initialState,
        applyMiddleware(logger)
      )
    );
    

    js/components/App.js

    import React, { Component } from 'react';
    import {
      StyleSheet,
      View,
      Switch
    } from 'react-native';
    
    
    export default class App extends Component {
    
      constructor(props) {
        super(props);
    
        this._handleSwitch = this._handleSwitch.bind(this);
      }
    
      _handleSwitch(value) {
        this.props.actions.triggerSwitch(value);
      }
    
      render() {
        const { switchState } = this.props;
    
        return (
          <View style={styles.container}>
            <Switch
              onValueChange={this._handleSwitch}
              value={switchState.currentValue}
            />
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
      },
    });
    

    js/containers/App.js

    import { bindActionCreators } from 'redux';
    import { connect } from 'react-redux';
    import { triggerSwitch } from '../actions/switchActions';
    import App from '../components/App';
    
    const mapStateToProps = state => ({
      switchState: state.switchState
    });
    
    const mapDispatchToProps = dispatch => ({
      actions: bindActionCreators({
        triggerSwitch
      }, dispatch)
    });
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(App);
    

    index.ios.js

    import React, { Component } from 'react';
    import { AppRegistry } from 'react-native';
    import { Provider } from 'react-redux';
    import createStore from './js/store';
    import App from './js/containers/App';
    
    const store = createStore();
    
    
    const SwitchTest = () => (
      <Provider store={store}>
        <App />
      </Provider>
    );
    
    AppRegistry.registerComponent('SwitchTest', () => SwitchTest);
    
    export default SwitchTest;
    

    package.json 依赖项

    "dependencies": {
        "react": "16.0.0-alpha.12",
        "react-native": "0.46.4",
        "react-redux": "^5.0.5",
        "redux": "^3.7.2",
        "redux-logger": "^3.0.6"
    },
    

    【讨论】:

      【解决方案3】:

      您是否正确绑定了_handleSwitch 函数? this.props.actions.triggerSwitch(value)派发好吗?

      使用redux-logger 在每个阶段检查您的状态和操作,并确保您的组件在切换后接收到正确的值。

      【讨论】:

      • 是的,在构造函数中,我确实运行了 redux-logger 并正确显示了 prev 和 next 状态。但是当我触发它时,它会自行回到false
      • 会不会是我的redux设置不正确?请看看这个stackoverflow.com/questions/39235637/…
      【解决方案4】:

      如果您使用 ES6 组件样式编写 react,例如 class Blah extends React,您需要使用 .bind(this) 来更改运行时范围。

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

      这可能有效。或关注@stereodenis 的评论。这也是一样的,但每次你的组件重新渲染时,它都会创建方法。

      【讨论】:

      【解决方案5】:

      我遇到了同样的问题。我的开关组件在 FlatList 中。尽管调用了 render() 方法,但开关值从未得到更新。结果,它恢复到旧值。

      为了解决这个问题,FlatList 需要一种方法来知道它需要重新渲染列表。 extraData 用于此目的。

      详情请参考here

      【讨论】:

        【解决方案6】:
        <Switch
          onValueChange={this._handleSwitch}
          value={(switch.currentValue) ? true : false}
        />
        

        这对我有用。

        【讨论】:

          【解决方案7】:

          对我来说,这是一个特定于 Android 的问题;无需此修改,它在 iOS 上运行良好。有必要中断默认事件传播并手动处理。这是一个使用钩子的示例,其中开关的逻辑在其他地方处理。你也可以在本地处理它

          [value, setValue] = useState()

          然后使用setValue 作为您的handler

          const Row = ({ onOff, handler, label }) => {
          const onSwitchChange = evt => {
            handler(evt.nativeEvent.value);
            // Interrupt the default event and handle it manually
            // Only necessary on Android but it also works on iOS so no branching necessary
            evt.stopPropagation();
          };
          
          return (
            <View style={styles.row}>
              <View style={styles.label}>
                <Text style={styles.labelText}>{label}</Text>
              </View>
              <Switch
                style={styles.switchSize}
                value={onOff}
                onChangeCapture={onSwitchChange}
              />
            </View>
          );
          };
          

          【讨论】:

            【解决方案8】:

            我遇到了类似的问题(与 redux 无关)。对我来说,当开关放置在 ListView 中并且 ListView 的数据源未在 Switch 的 onValueChange 中更新时,开关会立即变回。
            演示:https://rnplay.org/apps/tb6-fw
            固定:https://rnplay.org/apps/FfoVmg

            链接失效了,下面是 ListView 中的 Switch 代码不能正常工作:

            import React, { Component } from 'react';
            import {
              AppRegistry,
              StyleSheet,
              Text,
              View,
              ListView,
              Switch
            } from 'react-native';
            
            
            export default class SwitchTest extends Component {
            
              constructor(props) {
                super(props);
            
                const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
            
                this.state = {
                  switch1Value: false,
                  switch2Value: false,
                  dataSource: ds.cloneWithRows([{ data: 'some row data...'}]),
                };
              }
            
              render() {
                return (
                  <View style={styles.container}>
                    <Switch
                      value={this.state.switch1Value}
                      onValueChange={value => this.setState({ switch1Value: value })}
                    />
                    <View style={styles.listWrapper}>
                      <ListView
                        dataSource={this.state.dataSource}
                        renderRow={(rowData) => (
                          <View style={styles.row}>
                            <Text>{rowData.data}</Text>
                            <Switch
                              value={this.state.switch2Value}
                              onValueChange={value => this.setState({ switch2Value: value })}
                            />
                          </View>
                        )}
                      />
                    </View>
                  </View>
                );
              }
            }
            
            const styles = StyleSheet.create({
              container: {
                flex: 1,
                justifyContent: 'center',
                alignItems: 'center',
                backgroundColor: '#F5FCFF',
              },
              listWrapper: {
                height: 100,
                padding: 10
              },
              row: {
                flexDirection: 'row',
                alignItems: 'center'
              }
            });
            
            AppRegistry.registerComponent('SwitchTest', () => SwitchTest);
            

            解决方法是在 dataSource 中移动 switch2Value 状态并在 Switch 的 onValueChange 中更新 dataSource。

            // ...
            
            this.state = {
              switch1Value: false,
              dataSource: ds.cloneWithRows([{ data: 'some row data...', switch2Value: false }]),
            };
            
            // ...
            
                    renderRow={(rowData) => (
                      <View style={styles.row}>
                        <Text>{rowData.data}</Text>
                        <Switch
                          value={rowData.switch2Value}
                          onValueChange={value => this.setState({
                            dataSource: this.state.dataSource.cloneWithRows([{ ...rowData, switch2Value: value }])
                          })}
                        />
                      </View>
                    )}
            
            // ...
            

            【讨论】:

            • 嘿,链接失效了。也许下次消息中的 sn-p 会更好:)
            • @NicGutierrez,对不起,我已经在我的答案中添加了代码。
            猜你喜欢
            • 2013-09-18
            • 2015-10-03
            • 2017-07-29
            • 1970-01-01
            • 2021-11-10
            • 2017-12-07
            • 1970-01-01
            • 1970-01-01
            • 2015-02-22
            相关资源
            最近更新 更多