React Native + Redux:为什么 Switch 在切换到 true 后立即变回 false

React Native + Redux: Why does Switch immediately turns back to false after being switched to true?

本文关键字:true false Redux Native 为什么 Switch React      更新时间:2023-09-26

在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

谢谢!

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

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

我试图用 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/商店.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);

索引.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"
},

是否正确绑定了_handleSwitch函数?this.props.actions.triggerSwitch(value)调度可以吗?

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

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

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

这也许有效。 或遵循@stereodenis 的评论。这样做是一样的,但每次重新渲染组件时,它都会创建方法。

我遇到了同样的问题。我的交换机组件在平面列表中。虽然调用了 render(( 方法,但开关值从未更新过。结果,它恢复到旧值。

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

有关详细信息,请参阅此处。

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

这对我有用。

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

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

链接不再工作,因此以下是列表视图中的切换无法正常工作的代码:

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);

解决方法是在数据源中移动 switch2Value 状态,并在 Switch 的 onValueChange 中更新数据源。

// ...
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>
        )}
// ...