React-当我单击其他页面时如何更改状态?

时间:2019-02-28 19:36:14

标签: reactjs

在我的程序中,我有各种各样的卡片都放在屏幕上,上面有人物的图片和有关该人物的信息。我正在从API中提取所有这些信息,并负责执行客户端分页以一次仅在屏幕上显示几张卡片。

这是我的代码:

genCard = () => {

const { isLoaded, items, currentPage, totalNumberPages, recordsPerPage } = this.state;
if (isLoaded) {
 let returnCard = [];
 let i;
 for(i = currentPage; i < recordsPerPage; i++) {
  returnCard.push(<Card key={items[i].id} cardName={items[i].name} imgSrc={items[i].image} birthYear={items[i].birth_year}/>);

 }
 return returnCard;
}
return null;

};

  handlePageClick = (data) => {
    let selected = data.selected;
    let offset = Math.ceil(selected * this.props.perPage);
this.setState({

})

};

如您所见,我使用的是for循环,一次只能在屏幕上显示10个项目(卡片)。我想做的是当您单击另一页时,我希望它重新呈现并在屏幕上显示其他卡片。

那么,我该怎么做呢?如何将状态设置为您单击的页面,以便将正确的卡片呈现在屏幕上?

在此先感谢您的帮助。希望这是有道理的。

3 个答案:

答案 0 :(得分:1)

更新:除了下面的JSFiddle链接之外,还在此处添加代码段。

function Card(props) {

	return <div>I'm card {props.id}</div>
}

class Cards extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
    	isLoaded: false,
      items: [1,2,3,4,5,6,7,8,9,10, 11, 12, 13, 14, 15],
      currentPage: 1,
      recordsPerPage: 5,
      itemStartIndexOnPage: 0,
      tempCount: 0
    }
  }
  
  getCards = () => {
    const {items, itemStartIndexOnPage, recordsPerPage} = this.state;
    const itemsForThisPage =  items.slice(itemStartIndexOnPage, itemStartIndexOnPage + recordsPerPage);
    let cards = itemsForThisPage.map((item, i) => <Card key={item} id={item} {...item} />)
    return cards;
  }
  
  handlePageClick = (data) => {
  	let selected = data;
    let offset = Math.ceil((selected - 1) * this.state.recordsPerPage);
    this.setState({
    	currentPage: selected,
      itemStartIndexOnPage: offset
    })
  }
  
  render() {
    return (
      <div>
       Page No: {this.state.currentPage}
       {this.getCards()}
       <button onClick={() => this.handlePageClick(1)}>Page 1</button>
       <button onClick={() => this.handlePageClick(2)}>Page 2</button>
       <button onClick={() => this.handlePageClick(3)}>Page 3</button>
      </div>
    )
  }
}

ReactDOM.render(<Cards />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

如果您正在寻找的话,我已经写了这个snippet

setState重新渲染组件树。因此,在单击分页按钮时调用handlePageClick,然后可以在getCards()函数中调用render(),该函数将更新组件中的卡。

如果需要在页面单击时从API端点获取项的子集,则可以使handlePageClick异步,并在setState之后或await中调用then。如果您要一次获取所有数据,则可以在componentDidMount中进行操作并存储在状态中。

答案 1 :(得分:0)

您可以在handlePageClick中添加以下内容

this.setState({ currentPage: selected })

并如下更改genCard

 const startIndex = currentPage * recordsPerPage
 for(i = startIndex i < startIndex + recordsPerPage; i++) {
  returnCard.push(<Card key={items[i].id} cardName={items[i].name} 
  imgSrc={items[i].image} birthYear={items[i].birth_year}/>);
 }

答案 2 :(得分:0)

好吧,说实话,您可以访问状态时似乎已经能够进行足够的反应,并且您正在将某些项目渲染为子项,所以目前阻止您定义onClick的原因是处理程序将调用类定义的函数(使用您看似使用的类属性)还是通过将方法绑定到构造函数中?

是否只是您缺少寻呼机,以便您实际上可以委托事件?

我认为Sai Sandeep Vaddis answer已经为您提供了有关如何处理分页数据的一些信息,这是我要添加的唯一一件事,即如何构造多个组件以使其在整个应用程序中可重复使用。 / p>

现在,请记住,关于您的宏伟设计,我没有任何线索,并且您显示的代码(除了您的循环并没有真正开始正确之外)似乎还在工作

const { Component } = React;

// overly basic pager
const Pager = ({ page, pageSize, itemCount, onPageChange }) => {
  const totalPages = Math.ceil( itemCount / pageSize );
  const children = [];
  for (let i = 0; i < totalPages; i++) {
    children.push( <span key={ i } onClick={ () => onPageChange(i) } className={ classNames( 'page-item', { selected: i === page } ) }>{ i + 1 }</span> );
  }
  return <div className="pager">{ children }</div>;
};

// a component to keep a local state (current page state, and all the items it holds)
class PagedList extends Component {
  constructor() {
    super();
    this.state = {
      page: 0
    };
    this.changePage = this.changePage.bind( this );
  }
  changePage( targetPage ) {
    this.setState( { page: targetPage } );
    // this could theoretically inform any parent that the page has changed
    // in case this should be tracked from the parent (in which case having
    // the page as state doesn't really make sense anymore, it was just a 
    // quick way to enable paging for already retrieved data
  }
  render() {
    const { page = 0 } = this.state;
    // some default settings on this.props (can be done with getDerivedProps or getDefaultProps as well)
    const { items, pageSize = 10, component = props => JSON.stringify(props) } = this.props;
    // no items yet, wait for the next render I suppose
    if (!items) {
      return <div>No items available</div>;
    }
    // render pager / current page items / pager
    return <div>
      <Pager page={ page } pageSize={ pageSize } itemCount={ items.length } onPageChange={ this.changePage } />
      { items.slice( page * pageSize, (page * pageSize) + pageSize ).map( item => React.createElement( component, {...item, key: item.id } ) ) }
      <Pager page={ page } pageSize={ pageSize } itemCount={ items.length } onPageChange={ this.changePage } />
    </div>
  }
}

// very basic component displaying the name of the breed
const BreedItem = ({ name }) => <div>{ name }</div>;

// component that will load a random image when originally loaded
// could probably use a flag that says if it's still mounted before updating the state
// but, it's simply good to be aware of it :)
class BreedCard extends Component {
  constructor() {
    super();
    this.state = {
    };
  }
  componentDidMount() {
    const { name } = this.props;
    fetch( `https://dog.ceo/api/breed/${name}/images/random` )
      .then( response => response.json() )
      .then( data => this.setState( { image: data.message } ) );
  }
  render() {
    const { name } = this.props;
    const { image } = this.state;
    return <div className="card">
      <BreedItem name={ name } />
      { image ? <img src={ image } alt={ name } /> : <span>loading image</span> }
    </div>;
  }
}

// gets the items from the api (all at once to be fair)
// and then renders them in a PagedList
// doesn't really have a clue about what page it in, just decides on
// - pageSize
// - data
// - component to render
class BreedList extends Component {
  constructor() {
    super();
    this.state = {
      items: []
    };
  }
  componentDidMount() {
    fetch( 'https://dog.ceo/api/breeds/list/all' )
      .then( response => response.json() )
      .then( data => this.setState( { items: Object.keys( data.message ).map( key => ({ id: key, name: key }) ) } ) );
  }
  render() {
    const { items } = this.state;
    
    return <PagedList pageSize={10} items={items} component={ BreedCard } />;
  }
}

// lets get cracking then :)
const container = document.querySelector('#container');
ReactDOM.render( <BreedList />, container );
.page-item {
  display: inline-block;
  padding: 5px;
  cursor: pointer;
  border-radius: 5px;
}
.page-item:hover {
  background: silver;
}
.selected {
  background-color: blue;
  color: white;
}
.pager {
  border: solid black 1px;
  display: flex;
  flex-direction: row;
  font-size: .8em;
}
.card {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  border: solid #a0a0a0 1px;
  margin: 3px;
  padding: 5px;
}
.card > img {
  max-width: 320px;
  max-height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script id="classnames" src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.js"></script>
<div id="container"></div>