今天實作首頁的顯示舊文章的按鈕功能。
在實作前想先介紹react的hook這個東西:
在去年以前,react的組件實作都是使用Class component,也就是以class的方式來定義組件,
而如果使用function的方式的話,則是Function component。
function component雖然好用,但卻有著無法使用state和生命週期函式的問題,因此又稱為無狀態組件。
但class component卻有著一些問題,例如會讓組件結構變得複雜以及以及不好管理,以及無法紀錄某個瞬間的組件狀態,因此才有了hook的出現。
hook這個工具允許我們以function component來使用state跟生命週期函式,且更可以客製化自己的hook,使得組件的運算邏輯和狀態更加容易管理。
文章中不會有詳細的說明,有興趣的可參閱:
函式組件的確比起類別組件更容易去使用,且不用擔心this綁定的問題,目前官方跟主流都是推薦使用hook+function component去做開發。
因為我們的index.js是使用function component,因此,我將使用useState這個hook,來實作組件的狀態。
用法:
要藉由hook使用state,我們只需要下以下的語法:
const [狀態名, 設定狀態的函式名] = useState(狀態初始值)
這個語法會回傳一個陣列,第一項是我們的state,第二項則是類似原本setState功能的一個函式但用法稍有不同。
原先class component的setState傳入的是一個物件,且可以只傳入部分的key值下去更新,而hook的則不同你只需要傳入一個全新的狀態值即可,且狀態的值跟初始值也不限於物件,因此使用上更自由。
詳細可參考官方教學
先秀出修改完的程式碼:
src/page/index.js
import React, { useState } from "react"
import Layout from "../components/layout"
import PostPreview from "../components/post-preview"
const IndexPage = (props) => {
/*
使用useState這個hook,將chunk訂為state,並且setChunk為更改chunk的函式。
postBoby根據chunk*5的數量來顯示文章,每按一次按鈕,多5篇文章。
*/
const [chunk, setChunk] = useState(1)
const head = props.data.allContentfulHead.edges[0].node
const posts = props.data.allContentfulPost.edges
const postBoby = posts.slice(0, 5*chunk).map((post)=><PostPreview key={post.node.slug} data={post.node}/>)
return (
<Layout>
<header className="masthead" style={{backgroundImage: `url('${head.image.file.url}')`}}>
<div className="overlay"></div>
<div className="container">
<div className="row">
<div className="col-lg-8 col-md-10 mx-auto">
<div className="site-heading">
<h1>{head.title}</h1>
<span className="subheading">{head.subTitle}</span>
</div>
</div>
</div>
</div>
</header>
<div className="container">
<div className="row">
<div className="col-lg-8 col-md-10 mx-auto">
{postBoby}
{/* 根據顯示的文章數是否小於總文章數,來決定按鈕是否顯示 */}
<div className="clearfix" style={{display: 5*chunk<posts.length ? 'block' : 'none' }}>
{/* 按下按鈕,觸發setChunk,將chunk變為chunk+1,並且因為state改變,因此組件觸發重新渲染 */}
<a className="btn btn-primary float-right" onClick={() => setChunk(chunk + 1)}>Older Posts →</a>
</div>
</div>
</div>
</div>
<hr/>
</Layout>
)}
export default IndexPage
export const indexQuery = graphql`
query indexQuery {
allContentfulHead {
edges {
node {
image {
file {
url
}
}
title
subTitle
}
}
}
allContentfulPost(sort: { fields: [publishDate], order: DESC }) {
edges {
node {
slug
title
subTitle
publishDate(formatString: "MMMM Do, YYYY")
author {
name
}
body {
childMarkdownRemark {
excerpt
}
}
}
}
}
}
`
我們修改了幾個地方:
.slice(0, 5*chunk)
去把原先的文章陣列取出5*chunk數的文章做顯示。 /*
使用useState這個hook,將chunk訂為state,並且setChunk為更改chunk的函式。
postBoby根據chunk*5的數量來顯示文章,每按一次按鈕,多5篇文章。
*/
const [chunk, setChunk] = useState(1)
const head = props.data.allContentfulHead.edges[0].node
const posts = props.data.allContentfulPost.edges
const postBoby = posts.slice(0, 5*chunk).map((post)=><PostPreview key={post.node.slug} data={post.node}/>)
{/* 根據顯示的文章數是否小於總文章數,來決定按鈕是否顯示 */}
<div className="clearfix" style={{display: 5*chunk<posts.length ? 'block' : 'none' }}>
{/* 按下按鈕,觸發setChunk,將chunk變為chunk+1,並且因為state改變,因此組件觸發重新渲染 */}
<a className="btn btn-primary float-right" onClick={() => setChunk(chunk + 1)}>Older Posts →</a>
allContentfulPost(sort: { fields: [publishDate], order: DESC }) {
如此便完成了更舊文章
的功能了!!
點下去
成功!