Thinking in React Implemented by Reagent
前言
本文是学习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"))
总结
尊重原创,转载请注明
参考
https://reactjs.org/docs/thinking-in-react.html
- struts的声明式异常处理 demo
- npm管理工具介绍
- 对windows密码抓取神器mimikatz的逆向分析
- Keras中神经网络模型的5阶段生命周期
- java的断言(assert)
- Android studio中Rendering Problems不能可视化操作的解决办法
- 使用 Referer Meta 标签控制 referer—详解 referrer-policy
- 网站抓取引子 - 获得网页中的表格
- Android Firebase 服务简介
- CVE-2015-0393:Oracle发布严重安全漏洞预警
- 研究人员发现一种利用Siri窃取苹果iPhone/iPad数据的方法
- 关于Python中的__main__和编程模板
- 世界大战尽在掌控:盘点全球网络攻击实时追踪系统
- Activity数据传递
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Day10 :矩形覆盖
- vue3.0新特性初体验(二)
- 数据结构算法操作试题(C++/Python)——有效的括号
- 数据结构算法操作试题(C++/Python)——合并两个有序链表
- Day11 :二进制中1的个数
- 数据结构算法操作试题(C++/Python)——括号生成
- 数据结构算法操作试题(C++/Python)——合并K个排序链表
- 数据结构算法操作试题(C++/Python)——删除排序数组中的重复项
- 数据结构算法操作试题(C++/Python)——k个一组翻转链表
- 数据结构算法操作试题(C++/Python)——移除元素
- 数据结构算法操作试题(C++/Python)——实现strStr()
- 数据结构算法操作试题(C++/Python)——两数相除
- JavaScript 测试系列实战(三):使用 Mock 模拟模块并处理组件交互
- 数据结构算法操作试题(C++/Python)——串联所有单词的子串
- TypeScript 实战算法系列(九):实现向量与矩阵