React状态不一致地从一个组件传递到下一个组件

时间:2019-05-30 15:56:41

标签: javascript reactjs react-native

我有两个组成部分:LeagueSelect和TeamSelect。

我现在要做的就是将“已检查的联盟”状态从LeagueSelect传递到TeamSelect。

当前已设置为,如果选中了相应的联赛,则请选中TeamSelect中的复选框。

问题:状态从LeagueSelect到TeamSelect不一致。

这是一个看起来像的视频: https://streamable.com/2i06g

如未在console.log中看到的那样,当未选中一个框时,状态将更新为“组中”,但是,当您再次尝试选中同一框时,状态将不在组中更新。

我最初尝试使用redux来实现这一点,以为这个问题是redux问题,转移到直接传递给子组件的状态,并意识到该问题一定在其他地方。

这是我的LeagueSelect组件:

import React, { Component } from 'react';
import { View, Text, Modal, TouchableHighlight, FlatList, Button } from 'react-native'
import { loadLeagues } from '../actions'
import { connect } from 'react-redux'
import Check from './CheckBox'
import axios from "axios"
import { loadCards, loadTeams, changeLeagues } from '../actions'
import { Icon } from 'native-base'
import TeamSelect from './TeamSelect'


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

    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: '' })
    }

    changeCheck = (index, acronym) => {
        //local variable to create query param
        firstString = []
        //adds to local variable if not checked, takes out variable if checked
        if(!this.state.checkedLeagues.includes(acronym)) {
            firstString.push(acronym)
        } else {
            firstString.filter(v => { return v !== acronym})
        }
        //adds leagues that could be in the current state that were not just passed in
        this.state.checkedLeagues.map(
            (v, i) => {
                if(v !== acronym) {
                    firstString.push(v)
                }
            }
        )

        //updates checked leagues state
        //makes api call with parameters set
        //prevents all leagues being unselected
        if(acronym === this.state.checkedLeagues[0] && firstString.length === 0) {
            this.setState({ checkMessage: `Don't you want something to look at?` })
        } else {
            if(!this.state.checkedLeagues.includes(acronym)){
                this.state.checkedLeagues[this.state.checkedLeagues.length] = acronym
                this.setState({ checkedLeagues: this.state.checkedLeagues })
            } else {
                newChecked  = this.state.checkedLeagues.filter(v => { return v !== acronym})
                this.setState({checkedLeagues: newChecked})
            }


            //updating the check
            this.state.checked[index] = !this.state.checked[index]
            this.setState({ checked: this.state.checked })

            queryString = []

            firstString.map(
                (v, i) => {
                    if (queryString.length < 1) {
                        queryString.push(`?league=${v}`)
                    } else if (queryString.length >= 1 ) {
                        queryString.push(`&league=${v}`)
                    }
                }
            )

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

    render() {


      return (

        <View style={{ position: 'relative'}}>
                        <Text
                            style={{
                                paddingTop: 8,
                                paddingLeft: 5,
                                fontSize: 15
                            }}
                        >Leagues</Text>
                        <View
                            style={{
                                flexDirection:"row",
                            }}
                        >
                            {this.props.league === null ?'' : this.props.league.map(
                                (v, i) => {
                                    return(
                                            <View 
                                                key={i}
                                                style={{
                                                    alignSelf: 'flex-end',
                                                    flexDirection:"row",
                                                    top: 4,

                                                }}
                                            >
                                                <Check
                                                    checked={this.state.checked[i]}
                                                    index={i}
                                                    value={v.acronym}
                                                    changeCheck={this.changeCheck}
                                                />
                                                <Text
                                                    style={{
                                                        paddingLeft: 23,

                                                    }}
                                                >{v.acronym}</Text>
                                            </View>
                                    )
                                }
                            )}
                        </View>
                    <Text
                        style={{
                            paddingLeft: 10,
                            paddingTop: 12,
                            fontStyle: 'italic',
                            color: '#F4AF0D'
                        }}
                    >{this.state.checkMessage}</Text>
                    <TeamSelect  checkedLeagues={this.state.checkedLeagues}/>
                </View>
            </View>
      );
    }
  }

export default LeagueSelect

这是我的TeamSelect组件:

import React, { Component } from 'react'
import { Text, View } from 'react-native'
import { connect } from 'react-redux'
import { loadTeams, loadLeagues } from '../actions'
import Check from './CheckBox'

class TeamSelect extends Component {
    constructor(props) {
        super(props)
        this.state = {
            checked: [],
            checkedTeams: [],
            setOnce: 0
        }
    }


    render() {
    console.log('in team', this.props.checkedLeagues)

        return(
          <View>
              { 
                  this.props.team === null ?'' : this.props.team.map(
                      (v, i) => {
                         return(
                                <View key={i}>
                                    <Check
                                        checked={ this.props.checkedLeagues.includes(v.league.acronym) ? true : false }
                                        index={i}
                                        value={v.team_name}
                                        changeCheck={this.changeCheck}
                                    />

                                { v.team_name === undefined ? null :
                                    <Text>{v.team_name}</Text>}
                                </View>
                            )
                      }
                  )
             }
          </View>
        )
    }
}


export default TeamSelect

1 个答案:

答案 0 :(得分:2)

this.setState({ checkedLeagues: this.state.checkedLeagues })

当您进行突变并将状态设置为同一对象时,此类语句可能会导致问题。对已检查联赛的引用不会更新,并且反应可能不会触发渲染。改用它

this.setState({ checkedLeagues: [...this.state.checkedLeagues] })

但是,解决问题的整个方法是错误的,您应该使用一个具有选中属性的Leagues对象,然后将其传递下去。

使您的联赛对象看起来像这样,

const leagues = [
  {
  acronym: 'abc',
  checked: false,
  teams: [ ...array of teams here ]
  },
  ...
]

当您将其传递到TeamSelect时,可以像这样映射它

const { leagues } = this.props
{leagues && leagues.map((league, i) => league.teams.map((team, j) (
<View key={team.team_name}>
 <Check
  checked={league.checked}
  index={i + i * j}
  value={team.team_name}
  changeCheck={() => this.changeCheck(i, j)}
 />
 {team.team_name && <Text>{team.team_name}</Text>}
</View>)))}

与LeagueSelect相同,您可以像这样映射联赛:

const { leagues } = this.state
{leagues.map((league, i) => (  
  <View 
    key={league.acronym}
    style={{
      alignSelf: 'flex-end',
      flexDirection:"row",
      top: 4,
    }}>
      <Check
        checked={league.checked}
        index={i}
        value={league.acronym}
        changeCheck={this.changeCheck}
      />
      <Text
       style={{
       paddingLeft: 23,
       }}
       >{league.acronym}</Text>
    </View>
  )
)}

注意:必须将联赛从道具复制到陈述状态,以便您对其进行变异。我只是键入了它,因此它在运行之前需要进行一些更改,这只是为了向您展示编码此代码的“反应方式”。

https://reactjs.org/docs/lifting-state-up.html