React Native (with Redux) animate Switch programma

2019-04-10 04:36发布

问题:

I have component which has Switch component inside. This whole component is clickable. When you click on this component, it fires dispatch (which modifies redux store) and causes this component to be re-rendered. What I need is to not re-render whole component but just to animate that Switch component to proper state.

Something like this

<TouchableOpacity onPress={onPress}>
    <Text>{this.props.title}</Text>
    <Switch value={this.state.active}/>
</TouchableOpacity>

Any idea?

I find this pretty common use case and I know I will get to a lot of similar scenarios so I need this behaviour but I can't find any examples or solutions on web. Maybe I am just missing something.

Thanks

--- EDIT ---

I also tried this. I tried to disable updating with shouldComponentUpdate, I tried to set state only in willRecieveProps and/or toggle, even without toggle. I also tried playing with LayoutAnimation. But nothing works. this.setState() just doesn't animate Switch component. I also tried playing with this._switch._rctSwitch.setNativeProps({ value: nextProps.active }) but this doesn't work either (and it smells because of _rctSwitch).

class ViewWithSwitchInside extends React.Component {
    constructor(props) {
        super(props)
        this.toggle = this.toggle.bind(this);
        this.state = {
            active: props.active
        }
    }

    componentWillReceiveProps(nextProps) {
        if (this.state.active != nextProps.active) {
            this.setState({ active: nextProps.active })
        }
    }

    toggle() {
        let newValue = !this.state.active
        this.setState({
            active: newValue,
        })
        if (typeof this.props.onClick === 'function') {
            this.props.onClick(newValue)
        }
    }

    render() {
        return (
            <TouchableWithoutFeedback onPress={this.toggle}>
                <View>
                    <Text>{this.props.title}</Text>
                    <Switch value={this.state.active} />
                </View>
            </TouchableWithoutFeedback>
        )
    }
}

回答1:

You cannot render just one children from your parent component. Even if you were somehow able to change the value of the variable that the Switch depends on, it won't show unless you re-render the parent component.

I attempted to use a global variable to set the value of the switcher, I also prevented the re-rendering of the parent component using shouldComponentUpdate() which you can read more on here

This are my results:

This is the initial state of my app. That global variable is set to false, and the parent component was already rendered.

Now I change the global variable to true by tapping on the View around the Switch and it changes

But nothing changes visually, only the variable changed, not the switcher.

I then change the state (an irrelevant change to the Switch component) by pressing the "Re-render" text at the bottom of the screen and this happens:

This is the code of the App above:

var isActive = false; //global variable
class Test extends Component {
  constructor(props) {
    super(props);
    this.state={active:false, irrelevantState: true};
  }

  test(foo){
    console.log("Attempting to change", foo);
    isActive = foo;
  }

  shouldComponentUpdate(nextProps, nextState){
    if(this.state.active != nextState.active){//If the new state is different than the previous, stop rendering.
      this.test(nextState.active);
      return false;
    }else { //else, re-render everything
      return true;
    }
  }

  render(){
    console.log("Test re-render");
    return(
      <View style={{flex:1}}>
        <TouchableOpacity onPress={()=>{this.setState({active:!this.state.active})}} style={{flex:1, marginTop:20}}>
          <Text>Test Component</Text>
          <Switch value={isActive}/>
        </TouchableOpacity>
        <TouchableOpacity onPress={()=>{this.setState({active:true})}} style={{flex:1, marginTop:20}}>
          <Text>Test Component</Text>
          <Switch value={isActive}/>
        </TouchableOpacity>
        <TouchableOpacity style={{height:20}} onPress={()=>this.setState({irrelevantState:false})}>
          <Text> Re-render</Text>
        </TouchableOpacity>
      </View>
    )
  }
}

Your best bet is to find a smart way to re-render only when this.state.active and other required states for your app to function change, disregarding other changes.