> For the complete documentation index, see [llms.txt](https://archive-ko.docs.klaytn.foundation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://archive-ko.docs.klaytn.foundation/content/dapp/tutorials/klaystagram/7.-feedpage/7-3.-feed-component.md).

# 7-3. Feed Component

![Klaystagram 피드](https://github.com/klaytn/klaytn-docs-ko/blob/main/docs/bapp/tutorials/klaystagram/images/klaystagram-feed.png)

1. `Feed` 컴포넌트의 역할
2. 컨트랙트 데이터 읽어오기: `getFeed` 메서드
3. 리덕스 스토어에 데이터 저장: `setFeed` 액션
4. 데이터 출력: `Feed` 컴포넌트

## 1) `Feed` component's role <a href="#id-1-feed-component-s-role" id="id-1-feed-component-s-role"></a>

[4. Klaystagram 스마트 컨트랙트 작성](/content/dapp/tutorials/klaystagram/4.-write-klaystagram-smart-contract.md)에서 `PhotoData` 구조체를 작성하고 이 구조체를 `_photoList` 맵핑 내에 위치시켰습니다. Feed 컴포넌트의 역할은 다음과 같습니다. 1. Read `PhotoData` via calling Klaystagram contract method (`redux/actions/photos.js`) 2. Show `PhotoData`(feed) with its owner information (`components/Feed.js`)

## 2) Read data from contract: `getPhoto` method <a href="#id-2-read-data-from-contract-getphoto-method" id="id-2-read-data-from-contract-getphoto-method"></a>

1. 컨트랙트 메서드를 호출합니다: `getTotalPhotoCount()`

   사진이 없는 경우 빈 배열과 함께 `setFeed` 액션을 호출합니다.
2. 컨트랙트 메서드를 호출합니다: `getPhoto(id)`

   사진이 있으면 각 사진 데이터를 프로미스로 가져와 feed 배열에 넣습니다. 모든 프로미스를 배열에 넣으면 feed 배열을 반환합니다.
3. 리덕스 액션을 호출합니다: `setFeed(feed)`

   feed 배열을 가져와 리덕스 스토어에 저장합니다.

```javascript
// src/redux/actions/photos.js

const setFeed = (feed) => ({
  type: SET_FEED,
  payload: { feed },
})

export const getFeed = () => (dispatch) => {
  // 1. 컨트랙트 메서드(READ) 호출: `getTotalPhotoCount()`
  //사진이 없는 경우 빈 배열과 함께 'setFeed' 액션을 호출합니다.
  KlaystagramContract.methods.getTotalPhotoCount().call()
    .then((totalPhotoCount) => {
      if (!totalPhotoCount) return []
      const feed = []
      for (let i = totalPhotoCount; i > 0; i--) {
        // 2. 컨트랙트 메서드(READ) 호출: `getPhoto(id)`
        // 사진이 있으면 모두 호출합니다.
        const photo = KlaystagramContract.methods.getPhoto(i).call()
        feed.push(photo)
      }
      return Promise.all(feed)
    })
    .then((feed) => {
      // 3. 액션 호출: `setFeed(feed)`
      // 사진 데이터(피드)를 리덕스 스토어에 저장합니다.
      dispatch(setFeed(feedParser(feed))
    })
}
```

## 3) Save data to store: `setFeed` action <a href="#id-3-save-data-to-store-setfeed-action" id="id-3-save-data-to-store-setfeed-action"></a>

After we successfully fetch photo data (feed) from the Klaystagram contract, we call `setFeed(feed)` action. 이 액션은 사진 데이터를 페이로드로 가져와 리덕스 스토어에 저장합니다.

## 4) Show data in component: `Feed` component <a href="#id-4-show-data-in-component-feed-component" id="id-4-show-data-in-component-feed-component"></a>

```javascript
// src/components/Feed.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import moment from 'moment'
import Loading from 'components/Loading'
import PhotoHeader from 'components/PhotoHeader'
import PhotoInfo from 'components/PhotoInfo'
import CopyrightInfo from 'components/CopyrightInfo'
import TransferOwnershipButton from 'components/TransferOwnershipButton'
import { drawImageFromBytes} from 'utils/imageUtils'
import { last } from 'utils/misc'

import * as photoActions from 'redux/actions/photos'

import './Feed.scss'

class Feed extends Component {
  constructor(props) {
    super(props)
    this.state = {
      isLoading: !props.feed,
    }
  }

  static getDerivedStateFromProps = (nextProps, prevState) => {
    const isUpdatedFeed = (nextProps.feed !== prevState.feed) && (nextProps.feed !== null)
    if (isUpdatedFeed) {
      return { isLoading: false }
    }
    return null
  }

  componentDidMount() {
    const { feed, getFeed } = this.props
    if (!feed) getFeed()
  }

  render() {
    const { feed, userAddress } = this.props

    if (this.state.isLoading) return <Loading />

    return (
      <div className="Feed">
        {feed.length !== 0
          ? feed.map(({
            id,
            ownerHistory,
            data,
            name,
            location,
            caption,
            timestamp,
          }) => {
            const originalOwner = ownerHistory[0]
            const currentOwner = last(ownerHistory)
            const imageUrl = drawImageFromBytes(data)
            const issueDate = moment(timestamp * 1000).fromNow()
            return (
              <div className="FeedPhoto" key={id}>
                <PhotoHeader
                  currentOwner={currentOwner}
                  location={location}
                />
                <div className="FeedPhoto__image">
                  <img src={imageUrl} alt={name} />
                </div>
                <div className="FeedPhoto__info">
                  <PhotoInfo
                    name={name}
                    issueDate={issueDate}
                    caption={caption}
                  />
                  <CopyrightInfo
                    className="FeedPhoto__copyrightInfo"
                    id={id}
                    issueDate={issueDate}
                    originalOwner={originalOwner}
                    currentOwner={currentOwner}
                  />
                  {
                    userAddress.toUpperCase() === currentOwner.toUpperCase() && (
                      <TransferOwnershipButton
                        className="FeedPhoto__transferOwnership"
                        id={id}
                        issueDate={issueDate}
                        currentOwner={currentOwner}
                      />
                    )
                  }
                </div>
              </div>
            )
          })
          : <span className="Feed__empty">No Photo :D</span>
        }
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  feed: state.photos.feed,
  userAddress: state.auth.address,
})

const mapDispatchToProps = (dispatch) => ({
  getFeed: () => dispatch(photoActions.getFeed()),
})

export default connect(mapStateToProps, mapDispatchToProps)(Feed)
```

At the first time, you can only see the text "No photo :D" because there is no photo data in contract yet.\
Let's make a UploadPhoto component to send photo data to contract!
