简体   繁体   English

我应该如何从道具实例化我的状态?

[英]How should I instantiate my state from props?

Reading around , I see that initializing state from props in the getInitialState()/constructor can be an anti-pattern. 左右 ,我看到从getInitialState()/构造道具初始化状态可以是一个反模式。

What is the best way of initializing state from props and managing to be consistent? 从道具初始化状​​态并设法保持一致的最佳方法是什么?

As you can see below, I'm trying to initialize my "Card" component so that I may have a likeCount and isLikedByMe states initialized. 如您在下面看到的,我正在尝试初始化我的“ Card”组件,以便可以初始化一个likeCountisLikedByMe状态。 I do this so that I may have a custom like counter displayed and the text of the Like button to change, by resetting the state. 我这样做是为了让我可以显示一个自定义的“喜欢”计数器,并可以通过重置状态来更改“喜欢”按钮的文本。

At this point, I'm doing this in the constructor, but that is the wrong way to do it. 在这一点上,我正在构造函数中执行此操作,但这是错误的方法。 How should I manage this? 我应该如何处理?

import * as React from "react";
import { CardLikeButton } from "./buttons";

export enum CardType {
    None = 0,
    Text,
    Image
}

export interface CardMedia {
    text?: string;
    imageUrl?: string;
}

export interface CardDetails {
    isLikedByMe: boolean;
    likeCount: number;
}


export interface CardParams extends React.Props<any> {
    cardType: number;
    cardId: string;
    cardMedia: CardMedia;
    cardDetails: CardDetails;
}

export class Card extends React.Component<CardParams, CardDetails> {

    state: CardDetails;

    constructor(props: CardParams) {
        super(props);

        console.log("in card constructor");
        console.log("card type: " + props.cardType);

        this.state = { // setting state from props in getInitialState is not good practice
            isLikedByMe: props.cardDetails.isLikedByMe,
            likeCount: props.cardDetails.likeCount
        };

    }

    componentWillReceiveProps(nextProps: CardParams) {
        this.setState({
            isLikedByMe: nextProps.cardDetails.isLikedByMe,
            likeCount: nextProps.cardDetails.likeCount
        });
    }

    render() {
        console.log("RENDERING CARD");
        // console.dir(this.props.cardDetails);
        // console.dir(this.props.cardMedia);
        // console.dir(this.props.cardType);

        if (this.props.cardType === CardType.Text) { // status card
            return (
                <div className="general-card">
                    <p>Text card.ID: {this.props.cardId}</p>
                    <p>{this.props.cardMedia.text}</p>
                    <CardLikeButton onButClick={this.likeButtonClicked} buttonText={this.state.isLikedByMe ? "Liked" : "Like"} isPressed={this.state.isLikedByMe}/>
                    <p>Like count: {this.state.likeCount}</p>
                </div>
            );
        } else { //photo card
            return (
                <div className="general-card">
                    <p>Image card.ID: {this.props.cardId}</p>
                    <p> {this.props.cardMedia.text} </p>
                    <img src={this.props.cardMedia.imageUrl} />
                    <br/>
                    <CardLikeButton onButClick={this.likeButtonClicked} buttonText={this.state.isLikedByMe ? "Liked" : "Like"} isPressed={this.state.isLikedByMe}/>
                    <p>Like count: {this.state.likeCount}</p>

                </div>
            );
        }
    }

    likeButtonClicked = () => {
        console.log('in card => like button clicked!');
        var _isLikedByMe = this.state.isLikedByMe;
        var _likeCount = this.state.likeCount;

        if (_isLikedByMe) {
            _likeCount--;
        } else {
            _likeCount++;
        }
        _isLikedByMe = !_isLikedByMe;

        this.setState({
            isLikedByMe: _isLikedByMe,
            likeCount: _likeCount
        })
    }
}

Here is the main list component: 这是主要的列表组件:

/// <reference path="../../typings/index.d.ts" />

import * as React from "react";
import * as ReactDOM from "react-dom";

import {Card} from "./card";

import {CardParams, CardType, CardMedia, CardDetails} from "./card";

var card1: CardParams = {
    cardType: CardType.Image,
    cardId: "card1234",
    cardDetails: {
        isLikedByMe: false,
        likeCount: 3
    },
    cardMedia: {
        text: "some test text; badescuga",
        imageUrl: "http://www9.gsp.ro/usr/thumbs/thumb_924_x_600/2016/06/19/738742-rkx1568-lucian-sinmartean.jpg"
    }
};

var card2: CardParams = {
    cardId: "card35335",
    cardType: CardType.Text,
    cardDetails: {
        isLikedByMe: true,
        likeCount: 1
    },
    cardMedia: {
        text: "some test 2 text"
    }
};

var cards = [card1, card2];

ReactDOM.render(
    <div>

        {
            cards.map((item) => {
                return (
                    <Card key={item.cardId} cardId={item.cardId} cardType={item.cardType} cardDetails={item.cardDetails} cardMedia={item.cardMedia}/>
                );
            })
        }
    </div>,
    document.getElementById("mainContainer")
);

Without getting into working with Flux, or Redux, and focusing on your question. 无需接触Flux或Redux,而是专注于您的问题。

IMHO, state and props need to be separated, where Card only gets props, and state is managed from above. 恕我直言,状态和道具需要分开,其中Card仅获得道具,并且状态是从上方进行管理的。 Card component will get an event handler to raise once the like button has been clicked. 单击“赞”按钮后, Card组件将获得引发事件处理程序的功能。 You could either do the "like" logic inside the Card component, and just raise the event handler with the output of that logic, for example: this.props.likeClicked(isLikedByMe, updatedLikeCount) . 您可以在Card组件内执行“喜欢”逻辑,然后仅使用该逻辑的输出引发事件处理程序,例如: this.props.likeClicked(isLikedByMe, updatedLikeCount) Or, do the whole logic in the parent component. 或者,在父组件中执行整个逻辑。 I would also wrap all cards in another component. 我还将所有卡包装在另一个组件中。

Example: 例:

class Card extends React.Component {
  constructor(props: CardParams) {
    super(props);
  }

  render() {
      return (  
        <div>
          <button onClick={this.likeButtonClicked}>
            {this.props.isLikedByMe ? 'Unlike' : 'Like'}
          </button>
          <p>Like count: {this.props.likeCount}</p>
        </div>
      )
   }

    likeButtonClicked = () => {
        console.log('in card => like button clicked!');
        var _isLikedByMe = this.props.isLikedByMe;
        var _likeCount = this.props.likeCount;

        if (_isLikedByMe) {
            _likeCount--;
        } else {
            _likeCount++;
        }

        _isLikedByMe = !_isLikedByMe;

        if (this.props.likeUpdated) {
          this.props.likeUpdated({
            cardId: this.props.cardId,
            isLikedByMe: _isLikedByMe,
            likeCount: _likeCount
          })
        }
    }
}

class CardList extends React.Component {
  constructor(props) {
    super(props) 

    this.state = {
                    // Could use es6 map
                    cards: {123: {isLikedByMe: false, likeCount: 3},
                            124: {isLikedByMe: true, likeCount: 2}}
                  }
  }

  _onLikeUpdated({cardId, isLikedByMe, likeCount}) {    
    const cards = Object.assign({}, this.state.cards)
    cards[cardId] = {isLikedByMe, likeCount}

    this.setState({cards})
  }

  _getCards() {
    return Object.keys(this.state.cards).map(cardId => {
      return <Card key={cardId}
                   cardId={cardId} 
                   likeUpdated={this._onLikeUpdated.bind(this)}   
                   {...this.state.cards[cardId]} />
    })
  }

  render() {
    return <div>
            {this._getCards()}
           </div>
  }
}

Fiddle: https://jsfiddle.net/omerts/do13ez79/ 小提琴: https : //jsfiddle.net/omerts/do13ez79/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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