首页 > 解决方案 > React-Native Imported Component 未在父状态更改时初始化其状态

问题描述

我正在 React-Native 上创建记忆卡游戏,我正在使用此存储库进行翻转动画https://github.com/lhandel/react-native-card-flip

在第 1 级,当我匹配所有卡时,我正在设置状态以更改级别,而在第 2 级卡仍然翻转,然后我希望处于初始状态。

解决前的Level 1: Level 1 Image

解决Level 1后的Level 2: Level 2 Image

我想要的是在第 2 级,它应该像最初一样渲染导入的 CardFlip 组件绘图卡背面(如在第 1 级中)。

我使用的是本机反应,而不是博览会。将我的代码放在 App.js 文件中并导入 card-flip 存储库并使用 react-native 运行 android 这段代码。

import React, { Component } from 'react';
import {
  StyleSheet,
  Image,
  View, Dimensions, Text,
  TouchableOpacity
} from 'react-native';

import CardFlip from 'react-native-card-flip';

export default class App extends Component<Props> {

  constructor(props){
      super(props)
      this.state = {
        page: true,
        level: 1,
        tries: 0,
      }
      this.width = Dimensions.get('window').width; 
      this.levels = {
        1:{cards:4,rowItems:2,tries:3,card_width:(this.width/2)-15},
        2:{cards:6,rowItems:2,tries:5,card_width:(this.width/2)-15},
        3:{cards:8,rowItems:2,tries:6,card_width:(this.width/2)-70},
        4:{cards:12,rowItems:3,tries:8,card_width:(this.width/3)-50},
        5:{cards:14,rowItems:3,tries:9,card_width:(this.width/3)-50},
        6:{cards:18,rowItems:3,tries:12,card_width:(this.width/3)-50},
        7:{cards:24,rowItems:4,tries:16,card_width:(this.width/4)-15},
        8:{cards:30,rowItems:5,tries:20,card_width:(this.width/5)-15},
        9:{cards:34,rowItems:5,tries:22,card_width:(this.width/5)-15},
        10:{cards:40,rowItems:5,tries:25,card_width:(this.width/5)-15}
      };
      this.dimens = {width: this.levels[this.state.level].card_width, height: this.levels[this.state.level].card_width};
  }

  go_next = () =>{
      this.setState({page: false, tries:0, level: 1});
  };
  go_home = () =>{
      this.setState({page: true});
  };

  tried = ()=>{ 
    this.setState({tries: this.state.tries+1});
  };
  level_up = ()=>{  
    console.log("level up: ", this.state.level+1);
    this.setState({level: this.state.level+1, tries: 0});
  };
  level_restart = ()=>{ 
    console.log("level restarted: ", this.state.level);  
    this.setState({level: this.state.level, tries: 0});
  }

  render() {
    if (this.state.page) {
        return (
          <View style={styles.container}>
            <Text style={styles.heading}>Cardy</Text>
            <Text>Color Memory Game for Kids</Text>
            <TouchableOpacity onPress={this.go_next}>
              <Image style={{marginBottom:30}} source={require("./assets/play.png")}></Image>
            </TouchableOpacity>
          </View>
        );
    } else {
      return (
        <View style={{flex:1}}>
            <View style={{flex:.1,alignItems: 'center',justifyContent:'flex-end',marginTop:20}}><Text style={styles.subheading}>Level {this.state.level}</Text><Text>Tries Remaining: {(this.levels[this.state.level].tries-this.state.tries)}</Text></View>
            
            <Cards dimens={this.dimens} cards={this.levels[this.state.level].cards} rowItems={this.levels[this.state.level].rowItems} tried={this.tried.bind(this)} level_up={this.level_up.bind(this)} level_restart={this.level_restart.bind(this)} level={this.state.level} tries={this.state.tries} total_tries={this.levels[this.state.level].tries} />

            <View style={{alignItems: 'center',flex:.1}}>
              <TouchableOpacity onPress={this.go_home}>
                <Text>&#8592; Go Home</Text>
              </TouchableOpacity>
          </View>
        </View>
      );
    }
  }
}

class Cards extends Component<props> {
  constructor(props){
      super(props)
      this.color='';
      this.ckey='';
      this.tries=0;
      this.matched=0;
      this.level = this.props.level;
  }

  shouldComponentUpdate(nextProps, nextState){
    if( this.level != nextProps.level || nextProps.tries==0 )
      return true;
    else return false;
  } 

  do_flip = (color, ckey)=>{
    if( this.color != '' ){ // second card flip
      this.props.tried();
      this.tries++;
      if( this.color == color ){  //card matched
        this.matched++;
        this["card"+ckey].flip();
        if( this.matched == this.props.cards/2 ){
          this.tries=0;
          this.matched=0;
          alert("Level completed successfully.");
          this.props.level_up(); 
        }
        else if( this.tries == this.props.total_tries ){
          alert("Sorry, Level failed, try again.");
          this.tries=0;
          this.matched=0;
          this.props.level_restart();
        }
      } else { // card not matched
        var pkey = this.ckey;
        this["card"+pkey].flip();
        if( this.tries == this.props.total_tries ){
          this.tries=0;
          this.matched=0;
          alert("Sorry, Level failed, try again.");
          this.props.level_restart();
        }
      }
      this.color='';
      this.ckey='';
    } else { // first card flip
      this["card"+ckey].flip();
      this.color=color;
      this.ckey=ckey;
    }
  };

  shuffleArray = (array) => {
      for (let i = array.length - 1; i > 0; i--) {
          const j = Math.floor(Math.random() * (i + 1));
          [array[i], array[j]] = [array[j], array[i]];
      }
  }

  generate_cards = ()=>{
    const inner_cards = this.props.rowItems;
    const main_loops = Math.ceil(this.props.cards/inner_cards);
    const colors = ["blue","green","red","yellow","black","aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","blanchedalmond","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkgrey","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategrey","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dimgrey","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","greenyellow","grey","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightgrey","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategrey","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","yellowgreen"];
    var selected_colors = colors.slice(0, this.props.cards/2);
    selected_colors = selected_colors.concat(selected_colors);
    this.shuffleArray(selected_colors);
    var cd = 0;
    let row = [];
    let card = [];
    var list = selected_colors.map( (color, key)=> {
      if( cd == 0 ) { card = []; }
      card.push(
        <CardFlip style={this.props.dimens} key={key} ref={(card) => this["card"+key] = card}>
          <TouchableOpacity activeOpacity={1} style={this.props.dimens} onPress={() => {this.do_flip(color,key); }} >
            <Image style={this.props.dimens} source={require("./assets/robot.png")}></Image>
          </TouchableOpacity>
          <TouchableOpacity activeOpacity={0} style={[this.props.dimens]}>
            <View style={[{backgroundColor:color}, this.props.dimens]}></View>
          </TouchableOpacity>
        </CardFlip>
      );
      cd++;
      if( cd==inner_cards || key == selected_colors.length-1 ){
        cd=0;
        return  <View key={key+"a"} style={{flex:1,flexDirection:'row',justifyContent:'space-evenly',alignItems: 'center'}}>{card}</View> 
      }
    }); 
    return list;
  }
  render(){
    return (
      <View style={{flex:.8}}>
      {this.generate_cards()}
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  heading:{
    fontSize: 40,
    color: 'green',
    fontWeight:'bold',
    marginBottom: 20
  },
  subheading:{
    fontSize: 30,
    color: 'green',
    fontWeight:'bold',
    marginBottom: 20
  },
  boxContainer: {
    flex: 1,
    alignItems: 'center',
    flexDirection: 'column',
    backgroundColor:'yellow'
  }
});

标签: react-native

解决方案


您正在初始化构造函数中的状态,该状态仅在组件安装时运行一次。因此,当父组件的状态发生变化时,它会将 props 传递给子组件,但子组件的构造函数在这种情况下并未运行,因此子组件状态的行为与您预期的不同。

像这样的东西会起作用。在您的子组件中定义componentWillReceiveprops,如果新收到的道具与前一个不同,则设置状态。

我正在使用库 lodash 来比较对象、数组等。我推荐你给我们

例子

import React, {Component} from 'react'
import _ from 'lodash';

class ChildComponent extends Component{
  componentWillReceiveProps(nextProps){ 
     if(!_.isEqual(this.props.something, nextProps.something) && Boolean(nextProps)){
        this.setState({something: nextProps.something})
     }
  }
  render(){
     return(

    )
  }
}

注意:尽量不要setState进入componentWillReceivePropscomponentDidUpdate没有条件,以确保它不会导致您进入设置状态的无限循环


推荐阅读