Thinking in React Implemented by Reagent

NO IMAGE
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

前言

 本文是學習Thinking in React這一章後的記錄,並且用Reagent實現其中的示例。

概要

構造恰當的資料結構
從靜態非互動版本開始
追加互動程式碼

一、構造恰當的資料結構

Since you’re often displaying a JSON data model to a user, you’ll find that if your model was built correctly, your UI (and therefore your component structure) will map nicely.

 VDom讓我們可以將Model到View的對映交出,而更專注於資料和資料結構本身,即是折騰資料和資料結構才是我們的主要工作。因此我們要設計出與View中元件結構對應的資料結構,然後將不符合該資料結構的資料做一系列轉換,然後將資料交給React就好了。
 居上所述那麼可以知道,資料結構就依賴View的結構,那麼如何設計View的結構呢?是採用Top-down還是Bottom-up的方式呢?對於小型應用我們直接採用Top-down即可,對於大型應用則採用Bottom-up更合適。(根據過往經驗將大規模的問題域拆分成多個小規模的問題域,然後對小問題域採用Top-down方式,若無法直接採用Top-down方式則繼續拆分,然後將多個小問題域的值域組合即可得到大問題域的值域)
 無論是Top-down還是Bottom-up方式,都要將View構建為樹結構(這很符合DOM結構嘛)。因此得到如下結構

FilterableProductTable
|_SearchBar
|_ProductTable
|_ProductCategoryRow
|_ProductRow

 而資料則從頂層View元件往下流動,各層提取各自資料進行渲染。

二、從靜態非互動版本開始

It’s best to decouple these processes because building a static version requires a lot of typing and no thinking, and adding interactivity requires a lot of thinking and not a lot of typing.

 從設計(他人或自己)那得到設計稿或HTML模板,我們就可以開始著手重構模板、新增互動效果和填充業務邏輯和服務端互動等功能了。且慢,我們先不著急動手,而是要先分清工作步驟,才能有條不紊地包質保量工作哦!

目標:得到符合React規範的View結構

目標:得到最低標準的可互動的React應用

目標:補充業務邏輯,細化互動

目標:連線遠端資料來源,細化互動

(ns demo.core
(:require [reagent.core :as re])
(def products [
{:category "Sporting Goods", :price "$49.99", :stocked true, :name "Football"}
{:category "Sporting Goods", :price "$9.99", :stocked true, :name "Baseball"}
{:category "Sporting Goods", :price "$29.99", :stocked false, :name "Basketball"}
{:category "Electronics", :price "$99.99", :stocked true, :name "iPod Touch"}
{:category "Electronics", :price "$399.99", :stocked false, :name "iPhone 5"}
{:category "Electronics", :price "$199.99", :stocked true, :name "Nexus 7"}
])
(declare <filterable-product-table>
<search-bar>
<product-table>
<product-category-row>
<product-row>)
(declare get-rows)
(defn <filterable-product-table>
[products]
[:div
[<search-bar>]
[<product-table> products]])
(defn <search-bar>
[]
[:form
[:input {:placeholder "Search..."}]
[:input {:type "checkbox"}]
"Only show products in stock."])
(defn <product-table>
[products]
[:table
[:thead
[:tr
[:th "Name"]
[:th "Price"]]]
[:tbody
(get-rows products)]])
(defn assemble-rows
[products]
(reduce
(fn [{:keys [cate] :as rows-info} product]
(let [curr-cate (:category product)
curr-rows (if (not= curr-cate cate)
(list ^{:key curr-cate}[<product-category-row> curr-cate])
(list))
rows (cons ^{:key (:name product)} [<product-row> product] curr-rows)]
(-> rows-info
(assoc :cate curr-cate) ;; 更新cate值
(update
:rows
(fn [o rows]
(concat rows o))
rows)))) ;; 更新rows值
{:cate nil :rows '()}
products))
(defn get-rows
[products]
(-> (assemble-rows products)
:rows
reverse))
(defn <product-category-row>
[cate]
[:tr
[:td {:colSpan 2} cate]])
(defn <product-row>
[product]
[:tr
[:td (when (:stocked product) {:style {:color "red"}})
(:name product)]
[:td (:price product)]])


 這一步我們並沒有提供互動功能,因此只會用到prop傳遞資料,絕對不會用到state的。而互動的意思是,對View的操作會影響應用資料,從而重新整理View。

三、追加互動程式碼

 互動實質上就是觸發View狀態變化,那麼就必須提供一種容器來暫存當前View的狀態,而這個在React就是state了。

(ns demo.core
(:require [reagent.core :as re])
(def products [
{:category "Sporting Goods", :price "$49.99", :stocked true, :name "Football"}
{:category "Sporting Goods", :price "$9.99", :stocked true, :name "Baseball"}
{:category "Sporting Goods", :price "$29.99", :stocked false, :name "Basketball"}
{:category "Electronics", :price "$99.99", :stocked true, :name "iPod Touch"}
{:category "Electronics", :price "$399.99", :stocked false, :name "iPhone 5"}
{:category "Electronics", :price "$199.99", :stocked true, :name "Nexus 7"}
])
(declare <filterable-product-table>
<search-bar>
<product-table>
<product-category-row>
<product-row>)
(declare get-rows
validate-product)
(defn <filterable-product-table>
[products]
(let [search-text (re/atom "")
stocked? (re/atom false)
on-search-text-change #(reset! search-text (.. % -target -value))
on-stocked?-change #(reset! stocked? (.. % -target -checked))]
(fn []
[:div
[<search-bar> on-search-text-change on-stocked?-change]
[<product-table> (filter (partial validate-product @search-text @stocked?) products)]])))
(defn validate-product
[search-text stocked? product]
(and (if stocked? (:stocked product) true)
(as-> search-text %
(re-pattern (str "(?i)" %))
(re-find % (:name product)))))
(defn <search-bar>
[on-search-text-change on-stocked?-change]
[:form
[:input {:placeholder "Search..."
:onChange on-search-text-change}]
[:input {:type "checkbox"
:onChange on-stocked?-change}]
"Only show products in stock."])
(defn <product-table>
[products]
[:table
[:thead
[:tr
[:th "Name"]
[:th "Price"]]]
[:tbody
(get-rows products)]])
(defn assemble-rows
[products]
(reduce
(fn [{:keys [cate] :as rows-info} product]
(let [curr-cate (:category product)
curr-rows (if (not= curr-cate cate)
(list ^{:key curr-cate}[<product-category-row> curr-cate])
(list))
rows (cons ^{:key (:name product)} [<product-row> product] curr-rows)]
(-> rows-info
(assoc :cate curr-cate) ;; 更新cate值
(update
:rows
(fn [o rows]
(concat rows o))
rows)))) ;; 更新rows值
{:cate nil :rows '()}
products))
(defn get-rows
[products]
(-> (assemble-rows products)
:rows
reverse))
(defn <product-category-row>
[cate]
[:tr
[:td {:colSpan 2} cate]])
(defn <product-row>
[product]
[:tr
[:td (when (:stocked product) {:style {:color "red"}})
(:name product)]
[:td (:price product)]])

注意:reagent中使用state時,需要定義一個返回函式的高階函式來實現。

(defn <statefull-cmp> [data]
(let [local-state (re/atom nil)
on-change #(reset! local-state (.. % -target -value))]
(fn []
[:div
[:input {:onChange on-change}]
[:span @local-state]])))
(re/render [<statefull-cmp> 1]
(.querySelector js/document "#app"))

總結

 尊重原創,轉載請註明轉自:http://www.cnblogs.com/fsjohn… ^_^肥仔John

參考

https://reactjs.org/docs/thin…

相關文章

程式語言 最新文章