5

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>
        )
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
trubi
  • 295
  • 2
  • 14
  • Could you do something like this instead? http://stackoverflow.com/questions/40865391/how-to-show-text-yes-no-inside-a-switch-in-react-native – Tom Mar 06 '17 at 10:46
  • I am not sure what are you pointing to since that question has nothing with animations. But still, I edited my original question. It's minimal example, try it, it just doesn't animate. The change is animated ONLY and ONLY if you click directly on that switch. I am not sure if this is a bug but it looks like basic functionality to me. – trubi Mar 06 '17 at 15:40
  • @trubi: I may have the same problem but I don't understand your problem exactly. I have a switch that animates when you click it but I also want to switch it based on other user interactions. But when I just change it with `setState` it jumps instantly to the other position without the smooth animation. Is this different from your problem? – hippietrail Sep 25 '17 at 01:24
  • That sounds like exactly my problem. Do you have solution? – trubi Oct 03 '17 at 16:06

1 Answers1

0

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.

enter image description here

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

enter image description here

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:

Result of re-rendering

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.

Luis Rizo
  • 2,009
  • 4
  • 15
  • 34
  • Well, 1 global variable for every component where I need this sounds crazy. I don't need to re-render only one child component. Whole component could (and should) be re-rendered. All I need is to render that switch with animation. Or more like don't render at all, just set isOn() at that Switch. This is how native iOS switch works. It animates itself when you call this method. What about setNativeProps()? or should I implement my own native switch wrapper? – trubi Mar 03 '17 at 07:30