티스토리 뷰

반응형

단일 게시물 나타내기

Redux 스토어에 새 게시물을 추가 할 수있는 기능이 있으므로 게시물 데이터를 다양한 방식으로 사용하는 몇 가지 기능을 추가 할 수 있습니다.

 

현재 게시물 항목이 기본 피드 페이지에 표시되고 있지만 텍스트가 너무 길 경우 콘텐츠의 일부만 표시됩니다. 자체 페이지에서 단일 게시물 항목을 볼 수있는 기능이 있으면 도움이 될 것입니다.

 

단일 게시물 페이지 만들기

먼저 게시물 기능 폴더에 새 SinglePostPage 구성 요소를 추가해야합니다. 페이지 URL이 /posts/123과 같은 경우 React Router를 사용하여이 구성 요소를 표시합니다. 여기서 123 부분은 표시하려는 게시물의 ID 여야합니다.

 

// features/posts/SinglePostPage.js
import React from 'react'
import { useSelector } from 'react-redux'

export const SinglePostPage = ({ match }) => {
  const { postId } = match.params

  const post = useSelector(state =>
    state.posts.find(post => post.id === postId)
  )

  if (!post) {
    return (
      <section>
        <h2>Post not found!</h2>
      </section>
    )
  }

  return (
    <section>
      <article className="post">
        <h2>{post.title}</h2>
        <p className="post-content">{post.content}</p>
      </article>
    </section>
  )
}

 

React Router는 prop으로 URL 정보가 있는 match 객체를 전달할 것입니다. 이 컴포넌트를 렌더링하는 경로를 설정할 때 URL의 두 번째 부분을 match.params에서 postId라는 변수로 해당 값을 읽을 수 있습니다.

 

postId 값이 있으면 selector 함수를 사용하여 Redux 저장소에서 게시물 개체를 찾을 수 있습니다. state.posts는 모든 포스트 객체의 배열이어야한다는 것을 알고 있으므로 Array.find() 함수를 사용하여 해당하는 ID를 가진 게시물을 반환 할 수 있습니다.

 

useSelector에서 반환 된 값이 변경 될 때마다 컴포넌트가 다시 렌더링된다는 점에 유의해야합니다. 컴포턴트는 항상 store에서 필요한 데이터를 선택해야합니다. 이렇게하면 실제로 필요할 때만 렌더링하는 데 도움이됩니다.

 

스토어에 일치하는 게시물 항목이 없을 수 있습니다. 사용자가 URL을 직접 입력하려고했거나 올바른 데이터를 로드하지 않았을 수 있습니다. 이 경우 find() 함수는 실제 포스트 객체 대신 undefined를 반환합니다. 컴포넌트는 이를 확인하고 "포스트를 찾을 수 없습니다!"를 표시하여 처리해야합니다.

 

저장소에 올바른 게시물 개체가 있다고 가정하면 useSelector가 반환하고 이를 사용하여 페이지에서 게시물의 제목과 콘텐츠를 렌더링 할 수 있습니다.

 

이것이 <PostsList> 컴포넌트의 본문에 있는 로직과 상당히 유사하다는 것을 알 수 있을 것입니다. 여기에서 전체 posts 배열을 반복하여 기본 피드의 게시물을 표시합니다. 두 곳 모두에서 사용할 수있는 Post 컴포넌트를 추출해 볼 수 있지만, 포스트에서 필요한 부분을 표시하는 것과 전체 포스트를 표시하는 방법에는 약간의 차이가 있습니다. 일반적으로 약간의 중복이 있더라도 우선 별도로 작성하는 것이 좋습니다. 그런 다음 나중에 코드의 다른 섹션이 재사용 가능한 컴포넌트를 추출 할 수 있을지 나중에 결정할 수 있습니다.

 

단일 게시물 route 추가

이제 <SinglePostPage> 컴포넌트가 있으므로 이를 표시 할 경로를 정의하고 첫 페이지 피드의 각 게시물에 대한 링크를 추가 할 수 있습니다.

 

App.js에서 SinglePostPage를 가져오고 경로를 추가합니다.

// App.js
import { PostsList } from './features/posts/PostsList'
import { AddPostForm } from './features/posts/AddPostForm'
import { SinglePostPage } from './features/posts/SinglePostPage'

function App() {
  return (
    <Router>
      <Navbar />
      <div className="App">
        <Switch>
          <Route
            exact
            path="/"
            render={() => (
              <React.Fragment>
                <AddPostForm />
                <PostsList />
              </React.Fragment>
            )}
          />
          <Route exact path="/posts/:postId" component={SinglePostPage} />
          <Redirect to="/" />
        </Switch>
      </div>
    </Router>
  )
}

그런 다음 <PostsList>에서 해당 특정 게시물로 라우팅되는 <Link>를 포함하도록 수정합니다.

// features/posts/PostsList.js
import React from 'react'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'

export const PostsList = () => {
  const posts = useSelector(state => state.posts)

  const renderedPosts = posts.map(post => (
    <article className="post-excerpt" key={post.id}>
      <h3>{post.title}</h3>
      <p className="post-content">{post.content.substring(0, 100)}</p>
      <Link to={`/posts/${post.id}`} className="button muted-button">
        View Post
      </Link>
    </article>
  ))

  return (
    <section className="posts-list">
      <h2>Posts</h2>
      {renderedPosts}
    </section>
  )
}

이제 클릭을 통해 다른 페이지로 이동할 수 있으므로 <Navbar> 컴포넌트에 기본 게시물 페이지로 돌아가는 링크도 만들어 줍니다.

// app/Navbar.js
import React from 'react'

import { Link } from 'react-router-dom'

export const Navbar = () => {
  return (
    <nav>
      <section>
        <h1>Redux Essentials Example</h1>

        <div className="navContent">
          <div className="navLinks">
            <Link to="/">Posts</Link>
          </div>
        </div>
      </section>
    </nav>
  )
}

게시물 편집

기존 게시물의 id를 가져와서 store에서 해당 게시물을 읽은 후 제목과 내용을 수정하여 업데이트 할 수 있는 <EditPostForm> 컴포넌트를 만들어 보겠습니다.

 

게시물 업데이트

우선 postsSlice store가 실제로 게시물을 업데이트하는 방법을 알 수 있도록 새로운 reducer 함수와 액션을 추가해야합니다.

 

createSlice() 내에서 reducers 객체에 새 함수를 추가해야 합니다. 이 reducer의 이름은 어떠한 이벤트가 일어나는지 알 수 있도록 작성하는것이 중요합니다. reducer 이름은 이 액션이 전달 될 때마다 redux devtools에서 액션 타입의 일부로 표시되기 때문입니다. 첫번째 reducer는 postAdded라고 했기 때문에 이번에 새로 만들 함수는 postUpdated라고 하겠습니다.

 

게시물을 업데이트하려면 다음 사항을 알아야합니다.

  • state에서 올바른 게시물 객체를 찾을 수 있도록 업데이트할 게시물의 ID
  • 사용자가 입력한 새로운 제목과 내용

redux 액션 객체에는 type 필드가 있어야하며, 발생한 이벤트에 대한 세부 정보를 얻기 위해 또 다른 필드가 포함될 수 있습니다. 일반적으로 action.payload에 추가정보를 넣고, payload 필드에 문자열, 숫자, 객체, 배열 등을 사용해여 추가 할 수 있습니다. 게시물 업데이트의 경우 세가지 정보가 더 필요하프로 payload 필드 내부에 넣어서 액션 객체는 {type: 'posts/postUpdated', payload: {id, title, content}} 와 같이 생성하겠습니다.

 

기본적으로 createSlice에 의해 생성 된 액션 생성자는 하나의 인수를 받을 수 있습니다. 액션 생성자에 인자를 넣게 되면 해당 인자는 action.payload 필드로 액션 개체에 추가됩니다. 따라서 id, title, content의 값이 포함된 객체를 postUpdated 액션 생성자에게 인수로 전달할 수 있습니다.

 

또한 reducer는 액션이 전달 될 때 state가 어떻게 업데이트되어야 하는지 결정해야합니다. 이를 생각해서 reducer가 ID를 기반으로 올바른 게시물 객체를 찾고 해당 게시물의 text 및 content 필드를 업데이트 해야합니다.

 

마지막으로 사용자가 게시물을 저장할 때 UI가 새로운 postUpdated 액션을 전달할 수 있도록 createSlice가 생성 한 액션 생성기 함수를 내보내야합니다.

 

코드는 다음과 같습니다.

// features/posts/postsSlice.js
const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    postAdded(state, action) {
      state.push(action.payload)
    },
    postUpdated(state, action) {
      const { id, title, content } = action.payload
      const existingPost = state.find(post => post.id === id)
      if (existingPost) {
        existingPost.title = title
        existingPost.content = content
      }
    }
  }
})

export const { postAdded, postUpdated } = postsSlice.actions

export default postsSlice.reducer

이어서 게시물 수정 양식 생성하기를 배우겠습니다

 

반응형
댓글