本文是學習Thinking in React這一章後的記錄,而且用Reagent實現其中的示例。html
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.react
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組件往下流動,各層提取各自數據進行渲染。app
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模板,咱們就能夠開始着手重構模板、添加交互效果和填充業務邏輯和服務端交互等功能了。且慢,咱們先不着急動手,而是要先分清工做步驟,纔能有條不紊地包質保量工做哦!學習
(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。ui
交互實質上就是觸發View狀態變化,那麼就必須提供一種容器來暫存當前View的狀態,而這個在React就是state了。atom
(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時,須要定義一個返回函數的高階函數來實現。spa
(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/fsjohnhuang/p/7692956.html ^_^肥仔John設計
https://reactjs.org/docs/thinking-in-react.html