使用反应和打字稿仅切换地图数组的一个元素

Toggle only one element of map array with react and typescript

我正在映射评论 API 的数组,我想在点击“阅读更多”时只显示点击的评论,但目前正在扩展我的数组的所有评论,我我正在使用打字稿,这对我来说是全新的,所以我不知道如何移动,我应该如何将索引信息传递给我的状态?

interface State {

  reviews: Review[];
  isReadMore: boolean;
}
export default class MoviePage extends Component<{}, State> {
  state: State = {
    reviews: [],
    isReadMore: false,
  };
  componentDidMount() {
    this.asyncAwaitFunc();
    this.toggle(arguments);
  }
      asyncAwaitFunc = async () => {
        try {
          
          const reviewmovie = await axios.get<ReviewResponse>(
            `https://api.themoviedb.org/3/movie/${this.props.match.params.id}/reviews?api_key=`
          );
          this.setState({
    
            reviews: reviewmovie.data.results,
          });
        } catch (error) {}
      };


  toggle(index: any) {
    this.setState({
      isReadMore: !this.state.isReadMore,
    });


    
      render() {
        const { isReadMore, reviews } = this.state;

    
    
        return (
          <>
            
            <ReviewGrid>
              {reviews.map((review, index) => (
               
                   <ReviewContent key={index}>
                    {this.state.isReadMore
                      ? review.content.substring(0, 650)
                      : review.content}

                    <Button onClick={() => this.toggle(index)}>
                      {isReadMore ? "...read more" : " show less"}
                    </Button>
                  </ReviewContent>
                   
              ))}
            </ReviewGrid>
          </>
        );
    
      }
    }

由于我无权访问 API 或您正在使用的各种界面和组件,因此我对您的代码进行了一些修改以使其正常工作,但这应该能让您有所了解。您将 isReadMore 作为单个状态进行跟踪,这就是它对数组中的每个项目进行切换的原因。您需要为每个评论单独跟踪状态。这是可行的几种解决方案之一,但这里的想法是采用 API 的响应,并将其映射到一组新的对象,其中包括您将添加的新键 isReadMore,那么您可以为每条评论单独切换 属性。

Here 是 CodeSandbox 上的工作示例。

编辑: Here is a link 到第二个示例,它不需要您映射 api 调用的结果以添加新密钥跟踪 isReadMore 状态。此方法改为在 Map<Review, boolean> 中单独跟踪状态。 ES6 地图在这里工作得很好,因为您可以使用评论对象本身作为键,布尔值可以跟踪您的 hide/show 状态。


原解

interface Review {
    title: string;
    content: string;
}

interface MyReview extends Review {
    isReadMore: boolean;
}

interface State {
    reviews: MyReview[];
}

export default class MoviePage extends React.Component<{}, State> {
    state: State = {
        reviews: []
    };

    componentDidMount() {
        this.asyncAwaitFunc();
    }

    asyncAwaitFunc = () => {
        try {
            const reviewsFromApi: Review[] = [
                {
                    title: "Some Movie",
                    content: "some movie review that's pretty long"
                },
                {
                    title: "Some Other Movie",
                    content: "an even longer super long movie review that's super long"
                }
            ];

            this.setState({
                reviews: reviewsFromApi.map((r) => ({ ...r, isReadMore: false }))
            });
        } catch (error) {
            console.log(error);
        }
    };

    toggle(index: number) {
        this.setState({
            reviews: this.state.reviews.map((r, i) => {
                if (i === index) {
                    return {
                        ...r,
                        isReadMore: !r.isReadMore
                    };
                }

                return r;
            })
        });
    }

    render() {
        const { reviews } = this.state;

        return (
            <>
                <div>
                    {reviews.map((review, index) => (
                        <div key={index}>
                            <p>
                                {review.isReadMore
                                    ? review.content.substring(0, 10) + "..."
                                    : review.content}
                            </p>

                            <button onClick={() => this.toggle(index)}>
                                {review.isReadMore ? "Read more" : " Show less"}
                            </button>
                        </div>
                    ))}
                </div>
            </>
        );
    }
}

我认为问题在于您保存了一次 isReadMore,但您需要为每次评论保存 isReadMore。 这是一个例子:

interface ReviewRow {
  review: Review
  isReadMore: boolean
}
interface State {
  reviews: ReviewRow[]
}
export default class MoviePage extends Component<{}, State> {
  state: State = {
    reviews: []
  }

  componentDidMount() {
    this.asyncAwaitFunc()
  }

  asyncAwaitFunc = async () => {
    try {
      const reviewMovies = await axios.get<ReviewResponse>(
        `https://api.themoviedb.org/3/movie/${this.props.match.params.id}/reviews?api_key=`
      )
      this.setState({
        reviews: reviewMovies.data.results.map((review) => {
          return { review: review, isReadMore: false }
        })
      })
    } catch (error) {
      console.log(error)
    }
  }

  toggle(index: number) {
    const { reviews } = this.state
    reviews[index].isReadMore = !reviews[index].isReadMore
    this.setState({ reviews })
  }

  render() {
    const { reviews } = this.state
    return (
      <>
        <ReviewGrid>
          {reviews.map((reviewRow, index) => {
            ;<ReviewContent key={index}>
              { reviewRow.isReadMore ? reviewRow.review.content.substring(0, 650) : reviewRow.review..content}
              <Button onClick={() => this.toggle(index)}>{reviewRow.isReadMore ? '...read more' : ' show less'}</Button>
            </ReviewContent>
          })}
        </ReviewGrid>
      </>
    )
  }
}