• Thinking in React Implemented by Reagent


    前言

     本文是学习Thinking in React这一章后的记录,并且用Reagent实现其中的示例。

    概要

    1. 构造恰当的数据结构
    2. 从静态非交互版本开始
    3. 追加交互代码

    一、构造恰当的数据结构

    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模板,我们就可以开始着手重构模板、添加交互效果和填充业务逻辑和服务端交互等功能了。且慢,我们先不着急动手,而是要先分清工作步骤,才能有条不紊地包质保量工作哦!

    1. 目标:得到符合React规范的View结构
    2. 目标:得到最低标准的可交互的React应用
    3. 目标:补充业务逻辑,细化交互
    4. 目标:连接远程数据源,细化交互
    (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/fsjohnhuang/p/7692956.html _肥仔John

    参考

    https://reactjs.org/docs/thinking-in-react.html

  • 相关阅读:
    CSS 控制table 滑动及调整列宽等问题总结
    Java读取properties文件
    水晶报表打印
    C# 运行时序列化
    C#attribute-----------初级
    c# 单元测试工程如何取得当前项目路径
    C# 字段、属性、成员变量
    水晶报表主子报表分页问题
    从0打卡leetcode之day 3 -- 最大子序列和
    从零打卡leetcode之day 2---两数相加
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/7692956.html
Copyright © 2020-2023  润新知