實戰分析:評論功能(四)

目前爲止,第二階段知識已經基本介紹完,咱們已經具有了項目上手實戰必備的 React.js 知識,如今能夠把這些知識應用起來。接下來是實戰環節,咱們會繼續上一階段的例子,把評論功能作得更加複雜一點。css

咱們在上一階段的評論功能基礎上加上如下功能需求:html

  1. 頁面加載完成自動聚焦到評論輸入框。
  2. 把用戶名持久化,存放到瀏覽器的 LocalStorage 中。頁面加載時會把用戶名加載出來顯示到輸入框,用戶就不須要從新輸入用戶名了。
  3. 把已經發布的評論持久化,存放到瀏覽器的 LocalStorage 中。頁面加載時會把已經保存的評論加載出來,顯示到頁面的評論列表上。
  4. 評論顯示發佈日期,如「1 秒前」,」30 分鐘前」,而且會每隔 5 秒更新發布日期。
  5. 評論能夠被刪除。
  6. 相似 Markdown 的行內代碼塊顯示功能,用戶輸入的用 `` 包含起來的內容都會被處理成用 <code> 元素包含。例如輸入 `console.log` 就會處理成 <code>console.log</code> 再顯示到頁面上。

在線演示地址react

你們能夠在原來的第一階段代碼的基礎上進行修改,第1、二階段評論功能代碼能夠在這裏找到: react-naive-book-examples。能夠直接使用最新的樣式文件 index.css 覆蓋原來的 index.css。git

接下來能夠分析如何利用第二階段的知識來構建這些功能,在這個過程裏面可能會穿插一些小技巧,但願對你們有用。咱們回顧一下這個頁面的組成:github

咱們以前把頁面分紅了四種不一樣的組件:分別是 CommentApp 、CommentInputCommentListComment。咱們開始修改這個組件,把上面的需求逐個完成。瀏覽器

自動聚焦到評論框

這個功能是很簡單的,咱們須要獲取 textarea 的 DOM 元素而後調用 focus() API 就能夠了。咱們給輸入框元素加上 ref 以便獲取到 DOM 元素,修改 src/CommentInput.js 文件:app

...
    <textarea
      ref={(textarea) => this.textarea = textarea}
      value={this.state.content}
      onChange={this.handleContentChange.bind(this)} />
...

組件掛載完之後完成之後就能夠調用 this.textarea.focus(),給 CommentInput 組件加上 ComponentDidMount 生命週期:函數

 

class CommentInput extends Component {
  static propTypes = {
    onSubmit: PropTypes.func
  }

  constructor () {
    super()
    this.state = {
      username: '',
      content: ''
    }
  }

  componentDidMount () {
    this.textarea.focus()
  }
...

這個功能就完成了。如今體驗還不是很好,接下來咱們把用戶名持久化一下,體驗就會好不少。ui

你們能夠注意到咱們給原來的 props.onSubmit 參數加了組件參數驗證,在此次實戰案例中,咱們都會給評論功能的組件加上 propTypes 進行參數驗證,接下來就不累述。this

持久化用戶名

用戶輸入用戶名,而後咱們把用戶名保存到瀏覽器的 LocalStorage 當中,當頁面加載的時候再從 LocalStorage 把以前保存的用戶名顯示到用戶名輸入框當中。這樣用戶就不用每次都輸入用戶名了,而且評論框是自動聚焦的,用戶的輸入體驗就好不少。

咱們監聽用戶名輸入框失去焦點的事件 onBlur

...
    <input
      value={this.state.username}
      onBlur={this.handleUsernameBlur.bind(this)}
      onChange={this.handleUsernameChange.bind(this)} />
...

在 handleUsernameBlur 中咱們把用戶的輸入內容保存到 LocalStorage 當中:

class CommentInput extends Component {
  constructor () {
    super()
    this.state = {
      username: '',
      content: ''
    }
  }

  componentDidMount () {
    this.textarea.focus()
  }

  _saveUsername (username) {
    localStorage.setItem('username', username)
  }

  handleUsernameBlur (event) {
    this._saveUsername(event.target.value)
  }
...

在 handleUsernameBlur 中咱們把用戶輸入的內容傳給了 _saveUsername 私有方法(全部私有方法都以 _ 開頭)。_saveUsername 會設置 LocalStorage 中的 username 字段,用戶名就持久化了。這樣就至關於每當用戶輸入完用戶名之後(輸入框失去焦點的時候),都會把用戶名自動保存一次。

輸入用戶名,而後到瀏覽器裏裏面看看是否保存了:

而後咱們組件掛載的時候把用戶名加載出來。這是一種數據加載操做,咱們說過,不依賴 DOM 操做的組件啓動的操做均可以放在 componentWillMount 中進行,因此給 CommentInput 添加 componentWillMount 的組件生命週期:

...
  componentWillMount () {
    this._loadUsername()
  }

  _loadUsername () {
    const username = localStorage.getItem('username')
    if (username) {
      this.setState({ username })
    }
  }

  _saveUsername (username) {
    localStorage.setItem('username', username)
  }
...

componentWillMount 會調用 _loadUsername 私有方法,_loadUsername 會從 LocalStorage 加載用戶名而且 setState 到組件的 state.username 中。那麼組件在渲染的時候(render 方法)掛載的時候就能夠用上用戶名了。

這樣體驗就好多了,刷新頁面,不須要輸入用戶名,而且自動聚焦到了輸入框。咱們 一、 2 需求都已經完成。

小貼士

這裏插入一些小貼示,你們能夠注意到咱們組件的命名和方法的擺放順序其實有必定的講究,這裏能夠簡單分享一下我的的習慣,僅供參考。

組件的私有方法都用 _ 開頭,全部事件監聽的方法都用 handle 開頭。把事件監聽方法傳給組件的時候,屬性名用 on 開頭。例如:

<CommentInput onSubmit={this.handleSubmitComment.bind(this)} />

這樣統一規範處理事件命名會給咱們帶來語義化組件的好處,監聽(onCommentInput 的 Submit 事件,而且交給 this 去處理(handle)。這種規範在多人協做的時候也會很是方便。

另外,組件的內容編寫順序以下:

  1. static 開頭的類屬性,如 defaultPropspropTypes
  2. 構造函數,constructor
  3. getter/setter(還不瞭解的同窗能夠暫時忽略)。
  4. 組件生命週期。
  5. _ 開頭的私有方法。
  6. 事件監聽方法,handle*
  7. render*開頭的方法,有時候 render() 方法裏面的內容會分開到不一樣函數裏面進行,這些函數都以 render* 開頭。
  8. render() 方法。

若是全部的組件都按這種順序來編寫,那麼維護起來就會方便不少,多人協做的時候別人理解代碼也會一目瞭然。

相關文章
相關標籤/搜索