简体   繁体   中英

Child component inside modal unexpectedly resets to default state, React Native

I have a parent component--LeagueSelect--and a child component TeamSelect.

LeagueSelect is a React Native modal.

When I open LeagueSelect, make adjustments in the LeagueSelect modal, collapse the modal, and open it again, the state changes are preserved.

If I open LeagueSelect, make adjustments to TeamSelect in the LeagueSelect modal, collapse the modal, and open it again, the state changes are not preserved.

To provide context, when the modal (LeagueSelect) is open, this is what it looks like; the first red box is part of LeagueSelect, and the second red box is part of TeamSelect: 在此输入图像描述

This is my LeagueSelect component; I think the issue is happening in setModalVisible, because the console logs read the correct state when I console.log the state on componentWillUnmount:

class LeagueSelect extends Component {
    constructor(props) {
        super(props)
        this.state = {
            modalVisible: false,
            checked: [],
            checkedLeagues: [],
            uncheckedLeagues: [],
            checkMessage: '',
            firstString: []
        }
    }

    setModalVisible(visible) {
        this.setState({modalVisible: visible})
        if(this.state.checked.length === 0) {
            this.props.league.map(
                (v, i) => {
                    this.state.checked.push(true)
                    this.state.checkedLeagues.push(v.acronym)
                }
            )
        }
        this.setState({ checkMessage: '' })

        matchedTeams = []

        if(undefined !== this.props.teamObject) {
            this.props.teamObject.map(
                (v, i) => {
                    if(this.state.checkedLeagues.includes(v.league.acronym)){
                        matchedTeams.push(v.team_name)
                    }
                }
            )
        }

        queryString = []
            //encodes the teams that have their leagues selected
        matchedTeams.map(
            (v, i) => {
                if (queryString.length < 1) {
                    queryString.push(`?team=${v}`)
                } else if (queryString.length >= 1 ) {
                    queryString.push(`&team=${v}`)
                }
            }
        )

        queryString = queryString !== undefined ? queryString.join('') : ''

        axios.get(`http://localhost:4000/reports${queryString}`)
                .then(response => {
                    this.props.loadCards(response.data)
                })
    }

    componentDidMount() {
        this.props.loadLeagues()
        this.props.loadTeams() 
    }

    componentDidUpdate(){
        console.log(this.props.checkedLeagues)
        console.log('in league - in update - checked Teams', this.props.checkedTeams)
        this.props.changeLeagues(this.props.league, this.state.checkedLeagues, this.props.checkedTeams, this.props.queryTeams, this.props.teamObject)
    }

    render(){
      return (

        <View style={{ position: 'relative'}}>
              <Modal
                animationType="slide"
                transparent={false}
                visible={this.state.modalVisible}
                onRequestClose={() => {
                Alert.alert('Modal has been closed.');
                }}
              >

                <View
                    style={{
                        marginTop: 100
                    }}
                >

                    <TouchableHighlight
                        onPress={() => {
                            this.setModalVisible(!this.state.modalVisible);
                        }}
                    >
                        <Image
                            source={require('../assets/exit.png')}
                            style={{
                                height: 20,
                                width: 20,
                                left: 320,
                                resizeMode: 'contain'
                            }}
                        />
                    </TouchableHighlight>
                        <Text
                            style={{
                                paddingTop: 8,
                                paddingLeft: 5,
                                fontSize: 15,
                                fontFamily: 'Helvetica',
                                fontSize: 13
                            }}
                        >LEAGUE SELECT</Text>
                        <View
                            style={{
                                flexDirection:"row",
                                paddingLeft: 10
                            }}
                        >
                            {this.props.league === null ?'' : this.props.league.map(
                                (v, i) => {
                                    return(
                                            <View 
                                                key={i}
                                                style={{
                                                    alignSelf: 'flex-end',
                                                    flexDirection:"row",
                                                    top: 4,
                                                    paddingRight: 10
                                                }}
                                            >
                                                <Check
                                                    checked={this.state.checked[i]}
                                                    index={i}
                                                    value={v.acronym}
                                                    changeCheck={this.changeCheck}
                                                    style={{
                                                        paddingRight: 10
                                                    }}
                                                />
                                            </View>
                                    )
                                }
                            )}
                        </View>
                    <Text
                        style={{
                            paddingLeft: 10,
                            paddingTop: 12,
                            fontStyle: 'italic',
                            color: '#F4AF0D'
                        }}
                    >{this.state.checkMessage}</Text>
                    <TeamSelect />
                </View>
              </Modal>

              <TouchableHighlight
                onPress={() => {
                  this.setModalVisible(true);
                }}>
                <Icon
                    style={{
                        position: 'absolute',
                        top: -34,
                        right: 10,
                        fontSize: 25,
                        color: 'grey' 
                    }}
                    name='settings'
                />
              </TouchableHighlight>

            </View>
      );
    }
  }

function mapStateToProps(state) {
    return {
      league: state.league.league,
      team: state.team.team,
      checkedLeagues: state.league.checkedLeagues,
      checkedTeams: state.league.checkedTeams,
      queryTeams: state.league.queryTeams,
      teamObject: state.league.teamObject
     }
   }

export default connect(mapStateToProps, { loadCards, loadLeagues, loadTeams, changeLeagues })(LeagueSelect)

This is the TeamSelect component where the second red box's selections are made:

class TeamSelect extends Component {
    constructor(props) {
        super(props)
        this.state = {
            checked: [],
            checkedTeams: [],
            teamObject: [],
            queryString: [],
            accordionStatus: [true, true, true]
        }
    }

    componentDidMount(){
        console.log('did mount - checked Teams State', this.state.checkedTeams)
        //updates checked teams on load
        if(this.state.count === false && this.state.checkedTeams.length === 0 && this.state.checked.length === 0){
            this.setState({ count: true})
            this.props.team.map(
                (v, i) => {
                    if(this.props.checkedLeagues.includes(v.league.acronym)) {
                        this.state.checked.push(true)
                        this.state.checkedTeams.push(v.team_name)
                        this.state.teamObject.push(v)
                    } else if (this.state.checkedTeams !== undefined) {
                        this.state.checked.push(false)
                    }
                }
            )
            this.forceUpdate()
        }
    }

    componentWillUnmount(){console.log("unmount - count", this.state.count)
        console.log('will unmount - checked Teams State', this.state.checkedTeams)
    }

    componentDidUpdate(){
        this.props.changeLeagues(this.props.league, this.props.checkedLeagues, this.state.checkedTeams, this.state.queryString, this.state.teamObject)
    }

    changeCheck = (index, name, teamObject) => {
        //updates checked team state
        //prevents all teams being unselected
        if(name === this.state.checkedTeams[0]) {
            this.setState({ checkMessage: `Don't you want something to look at?` })
        } else {
            if(!this.state.checkedTeams.includes(name)){
                this.state.checkedTeams[this.state.checkedTeams.length] = name
                this.setState({ checkedTeams: [...this.state.checkedTeams] })
                //sets team object with new team object
                this.state.teamObject[this.state.teamObject.length] = teamObject
                this.setState({ teamObject: this.state.teamObject })
            } else {
                newChecked  = this.state.checkedTeams.filter(v => { return v !== name})
                this.setState({ checkedTeams: newChecked })
                //removes team object and sets new state
                newObjectChecked = this.state.teamObject.filter(v => { return v.team_name !== teamObject.team_name})
                this.setState({ teamObject: newObjectChecked })

            }

            //updates checkbox for specific space
            this.state.checked[index] = !this.state.checked[index]
            this.setState({ checked: this.state.checked })
        }
    }

    changeTeamSelect = (index) => {
        this.state.accordionStatus[index] = !this.state.accordionStatus[index]
        this.setState({ accordionStatus: this.state.accordionStatus})
    }

    render(){
        return(
            <View>
                <View>
                    <Text
                        style={{
                            fontFamily: 'Helvetica',
                            fontSize: 13,
                            paddingLeft: 5,
                            paddingBottom: 5
                        }}
                        onPress={()=> this.changeTeamSelect(0)}
                    >
                        NFL TEAM SELECT
                    </Text>
                </View>
                {
                    this.state.accordionStatus[0] === false ? null :
                        <View
                        style={{
                            flexDirection: 'row',
                            paddingLeft: 10,
                            flexWrap: 'wrap'
                        }}
                    >
                        { 
                            this.props.team.map(
                                (v, i) => {
                                    if(v.league.acronym === 'NFL'){
                                        return(
                                            <Check
                                                key={i}
                                                checked={ /*this.props.checkedLeagues.includes(v.league.acronym) ?*/ this.state.checked[i] /*: false*/ }
                                                index={i}
                                                teamObject={v}
                                                value={v.team_name}
                                                changeCheck={this.changeCheck}
                                            />
                                        )
                                    }

                                }
                            )

                        }
                    </View>
                }
                <View>
                    <Text
                        style={{
                            fontFamily: 'Helvetica',
                            fontSize: 13,
                            paddingLeft: 5,
                            paddingBottom: 5
                        }}
                        onPress={()=> this.changeTeamSelect(1)}
                    >
                        NBA TEAM SELECT
                    </Text>
                </View>
                {
                    this.state.accordionStatus[1] === false ? null :
                    <View
                        style={{
                            flexDirection: 'row',
                            paddingLeft: 10,
                            flexWrap: 'wrap'
                        }}
                    >
                        { 
                            this.props.team.map(
                                (v, i) => {
                                    if(v.league.acronym === 'NBA'){
                                        return(
                                            <Check
                                                key={i}
                                                checked={ /*this.props.checkedLeagues.includes(v.league.acronym) ?*/ this.state.checked[i] /*: false*/ }
                                                index={i}
                                                teamObject={v}
                                                value={v.team_name}
                                                changeCheck={this.changeCheck}
                                            />
                                        )
                                    }

                                }
                            )

                        }
                    </View>
                }
                <View>
                    <Text
                        style={{
                            fontFamily: 'Helvetica',
                            fontSize: 13,
                            paddingLeft: 5,
                            paddingBottom: 5
                        }}
                        onPress={()=> this.changeTeamSelect(2)}
                    >
                        MLB TEAM SELECT
                    </Text>
                </View>
                {
                    this.state.accordionStatus[2] === false ? null :
                    <View
                        style={{
                            flexDirection: 'row',
                            paddingLeft: 10,
                            flexWrap: 'wrap'
                        }}
                    >
                        { 
                            this.props.team.map(
                                (v, i) => {
                                    if(v.league.acronym === 'MLB'){
                                        return(
                                            <Check
                                                key={i}
                                                checked={ /*this.props.checkedLeagues.includes(v.league.acronym) ?*/ this.state.checked[i] /*: false */}
                                                index={i}
                                                teamObject={v}
                                                value={v.team_name}
                                                changeCheck={this.changeCheck}
                                            />
                                        )
                                    }

                                }
                            )

                        }
                    </View>
                }
            </View>
        )
    }
}

function mapStateToProps(state) {
    return {
      league: state.league.league,
      team: state.team.team,
      checkedLeagues: state.league.checkedLeagues,
      checkedTeams: state.league.checkedTeams,
      queryTeams: state.league.queryTeams
    }
}

export default connect(mapStateToProps, { loadCards, changeLeagues })(TeamSelect)

Something to note, I use redux to update the state, place it into a store, and then use it when the component is mounts again. What is interesting is that this.state.checkedLeagues preserves state as it should in the first component, however, this.state.checkedTeams does not preserve it's state in the second component. Any ideas why?

EDIT:

This is my LeagueReducer:

let defaultState = {
    league: null,
    checkedLeagues: null,
    checkedTeams: null,
    queryTeams: null,
    teamObject: null
}

export function LeagueReducer(state = defaultState, action){
    if(action.type === "CHANGE_LEAGUES") {
        return {
            ...state,
            league: action.league,
            checkedLeagues: action.checkedLeagues,
            checkedTeams: action.checkedTeams,
            queryTeams: action.queryTeams,
            teamObject: action.teamObject
        }
    } else {
       return state
    }
}

Looks like it's possible you're not updating the redux store correctly. Remember do not use array functions that mutate the array like push . You need to use functions like concat that return a new array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM