为什么当 redux 存储更新时本地状态也会改变

时间:2021-07-28 05:26:33

标签: javascript reactjs redux

现在我正在使用 react redux store 和 local store 构建一个应用程序。 我有两个组件“tweetTable_Comp”和“likeButton_Comp”。 redux store 拥有通过 API 获取的所有推文记录“tweets”,tweetTable_Comp 具有本地状态“filteredTweets”,以便稍后添加过滤功能并仅显示选定类型的推文。

并且每条推文都有喜欢的UserIds。 tweetTable_Comp 将喜欢的用户 ID 作为道具传递给 likeButton_Comp,以便它可以根据您是否已经喜欢这条推文来添加不同的样式。

这里的问题是,当用户按下类似按钮时,更改 redux 存储中的“tweets[indexNum].likingUserIds”也会影响本地状态“filteredTweets[indexNum].likingUserIds”。

我打算一一更改 redux 信息和本地状态信息,就像在已经运行良好的 deleteTweet 函数中一样。 但这不是故意的。

谁能告诉我为什么会这样?

这里是reducer.js redux tweets 有如下对象

・标题(字符串) ・文本(字符串) ・创建日期(字符串) ・likeingUserIds(array) ・userId(number)

const defaultState = {
  user: {
    loggedIn: false,
    id: 0,
    account: ''
  },
  tweets: []
}

export default function reducer(state = defaultState, action) {
  switch (action.type) {
    case 'UPDATE_TWEETS':
     return {
       ...state,
       tweets: action.tweets
     }
    default:
      return state;
  }
}

这里是 actions.js

export function getTweets(tweets){
  return {
    type: 'UPDATE_TWEETS',
    tweets: tweets
  }
}

这里是 tweetTable_Comp

class TweetTable_Comp extends Component{
  constructor(props){
    super(props)
    const {dispatch} = props;
    this.action = bindActionCreators(actions, dispatch);
    this.deleteButtonClicked = this.deleteButtonClicked.bind(this)
    this.editButtonClicked = this.editButtonClicked.bind(this)
    this.handleChanged = this.handleChanged.bind(this)
    this.state = {
      filteredTweets: [],
      searchWord: ""
    }
  }

  handleChanged(e){
    this.setState({[e.target.name]: e.target.value})
  }

  deleteButtonClicked(id, index){
    confirm("削除しますか?") &&
    this.deleteTweet(id, index)
  }

  editButtonClicked(id){
    this.props.history.push("/edit/" + id)
  }

  deleteTweet(id, index){
    fetch("http://localhost:8080/twitter/deleteTweet/" + id, {
      method: "DELETE"
    })
    .then((response) => {
      if(response.status === 200) {
        const newList = this.props.tweets.slice()
        newList.splice(index, 1)
        this.action.getTweets(newList)
        this.setState({filteredTweets: newList})
      }
    })
  }


  componentDidMount(){
    fetch("http://localhost:8080/twitter/sendAllTweets", {
      method: "GET"
    })
    .then((response) => {
      response.json()
      .then(json => {
        this.action.getTweets(json)
        this.setState({filteredTweets: json.slice()})
      })
    })
  }

  render(){
    return(
      <>
        <h1 className="text-center">tweet一覧</h1>
        <SearchBar searchWord={this.state.searchWord} handleChanged={this.handleChanged}/>
        <Container>
          <Row>
            <Col>
              <br/>
              <br/>
              <Table striped bordered hover>
                <thead>
                  <tr className="text-center">
                    <th>投稿日</th>
                    <th>投稿者</th>
                    <th>タイトル</th>
                    <th>内容</th>
                    <th>いいね</th>
                    <th>削除</th>
                    <th>編集</th>
                  </tr>
                </thead>
                <tbody>
                  { this.state.filteredTweets.map((tweet, index) => (
                    <tr key={tweet.id}>
                      <td className="text-center">{tweet.createdDate}</td>
                      <td className="text-center">{tweet.user.account}</td>
                      <td>{tweet.title}</td>
                      <td>{tweet.text}</td>
                      <td className="text-center">
                        <LikeButton likingUserIds={tweet.likingUserIds} index={index} id={tweet.id} />
                      </td>
                      <td className="text-center">
                        <Button variant="outline-secondary" onClick={() => this.deleteButtonClicked(tweet.id, index)}>
                          <FontAwesomeIcon icon={faTrashAlt} />
                        </Button>
                      </td>
                      <td className="text-center">
                        <Button variant="outline-secondary" onClick={() => this.editButtonClicked(tweet.id)}>
                          <FontAwesomeIcon icon={faEdit} />
                        </Button>
                      </td>
                    </tr>
                  ))
                  }
                </tbody>
              </Table>
            </Col>
          </Row>
        </Container>
      </>
    )
  }
}

TweetTable_Comp.propTypes = {
  dispatch: PropTypes.func,
  tweets: PropTypes.array,
  history: PropTypes.object,
  user:PropTypes.object
}

function mapStateToProps(state){
  return state
}

export default withRouter(connect(mapStateToProps)(TweetTable_Comp))

这里是像Button_Comp

class LikeButton_Comp extends Component {
  constructor(props){
    super(props)
    const {dispatch} = props
    this.action = bindActionCreators(actions, dispatch)
    this.likeButtonClicked = this.likeButtonClicked.bind(this)
  }

  likeButtonClicked(func, index){
    const data = {
      userId:this.props.user.id,
      tweetId:this.props.id
    }
    if(func === "unlike"){
      fetch("http://localhost:8080/twitter/like", {
        method: "DELETE",
        body: JSON.stringify(data)
      })
      .then((response) => {
          if(response.status === 200){
            let tweets = this.props.tweets.slice()
            const orgLikingUsers = this.props.tweets[index].likingUserIds.slice()
            const newLikingUsers = orgLikingUsers.filter(item => item !== this.props.user.id)
            tweets[index].likingUserIds = newLikingUsers
            this.action.getTweets(tweets)
          } else {
            alert("処理に失敗しました")
          }
        })
        .catch(error => console.error(error))
      } else {
        fetch("http://localhost:8080/twitter/like", {
          method: "POST",
          body: JSON.stringify(data)
        })
        .then((response) => {
          if(response.status === 200){
            let tweets = this.props.tweets.slice()
            let likingUsers = this.props.tweets[index].likingUserIds.slice()
            likingUsers.push(this.props.user.id)
            tweets[index].likingUserIds = likingUsers
            this.action.getTweets(tweets)
          } else {
            alert("処理に失敗しました")
          }
        })
        .catch(error => console.error(error))
      }
    }

  render(){
    return(
      <>
      <span>{this.props.likingUserIds.length}  </span>
      {this.props.tweets.length > 0 && this.props.likingUserIds.includes(this.props.user.id) ?
        <Button variant="outline-danger">
          <FontAwesomeIcon icon={faHeart} onClick={() => this.likeButtonClicked("unlike", this.props.index)}/>
        </Button> :
        <Button variant="outline-secondary">
          <FontAwesomeIcon icon={faHeart} onClick={() => this.likeButtonClicked("like", this.props.index)}/>
        </Button>
      }
      </>
    )
  }
}

LikeButton_Comp.propTypes = {
  dispatch: PropTypes.func,
  user: PropTypes.object,
  tweets: PropTypes.array,
  likingUserIds: PropTypes.array,
  index: PropTypes.number,
  id: PropTypes.number
}

function mapStateToProps(state){
  return state
}

export default withRouter(connect(mapStateToProps)(LikeButton_Comp))

0 个答案:

没有答案