React Native + Redux:为什么Switch切换到true后会立即返回false?

时间:2016-08-30 08:36:25

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

在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
  }
}

在减速器中:

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

谢谢!

8 个答案:

答案 0 :(得分:2)

并不是因为存在冗余,Switch才能正常工作,我们需要从状态显式设置value,否则立即将其设置回false

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

答案 1 :(得分:1)

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

<强> JS /动作/ actionTypes.js

export const TRIGGER_SWITCH = 'TRIGGER_SWITCH';

<强> JS /动作/ switchActions.js

import { TRIGGER_SWITCH } from './actionTypes';

export const triggerSwitch = value => ({
  type: TRIGGER_SWITCH,
  currentValue: value
});

<强> JS /减速器/ 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 /组件/ 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 /容器/ 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"
},

答案 2 :(得分:0)

您是否正确绑定了_handleSwitch功能? this.props.actions.triggerSwitch(value)发送好吗?

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

答案 3 :(得分:0)

如果您正在编写与class Blah extends React等ES6组件样式的反应,则需要使用.bind(this)来更改运行时范围。

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

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

答案 4 :(得分:0)

我面临着同样的问题。我的交换机组件位于FlatList中。尽管调用了render()方法,但开关值从未得到更新。结果,它恢复了原来的值。

为了解决这个问题,FlatList需要一种方法来知道它需要重新呈现列表。 Object.FindObjectOfType用于此目的。

有关详细信息,请参见here

答案 5 :(得分:0)

<Switch
  onValueChange={this._handleSwitch}
  value={(switch.currentValue) ? true : false}
/>

对我有用。

答案 6 :(得分:0)

对我来说,这是一个特定于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>
);
};

答案 7 :(得分:-1)

我遇到过类似的问题(与redux无关)。对我来说,当交换机放在ListView中并且Switch的onValueChange中没有更新ListView的dataSource时,交换机会立即切换回来。 演示: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>
        )}

// ...