前言

 本文是学习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

Thinking in React Implemented by Reagent的更多相关文章

  1. react与jQuery对比,有空的时候再翻译一下

    参考资料:http://reactfordesigners.com/labs/reactjs-introduction-for-people-who-know-just-enough-jquery-t ...

  2. [转] React Native Navigator — Navigating Like A Pro in React Native

    There is a lot you can do with the React Native Navigator. Here, I will try to go over a few example ...

  3. React源码解析:ReactElement

    ReactElement算是React源码中比较简单的部分了,直接看源码: var ReactElement = function(type, key, ref, self, source, owne ...

  4. react 插槽(Portals)

    前言: 昨天刚看了插槽,以为可以解决我工作中遇到的问题,非常激动,我今天又仔细想了想,发现并不能解决... 不过还是记录一下插槽吧,加深印象,嗯,就酱. 插槽作用: 插槽即:ReactDOM.crea ...

  5. 关于clojurescript+phantomjs+react的一些探索

    这两天需要使用phantomjs+react生成些图片 React->Clojurescript: 最开始发现clojurescript中包裹react的还挺多: https://github. ...

  6. React高级指南

    高级指南 1.深入JSX: 从本质上讲,JSX 只是为 React.createElement(component, props, ...children) 函数提供的语法糖. 因为 JSX 被编译为 ...

  7. react解析markdown文件

    当当当又get到了一个新技能,使用react-markdown来直接解析markdown文件(咳咳,小菜鸟的自娱自乐) 项目中遇到了一个API的那种展示方式,类似于入门手册啥的那种,如果是一个个调用接 ...

  8. Wait… What Happens When my React Native Application Starts? — An In-depth Look Inside React Native

    Discover how React Native functions internally, and what it does for you without you knowing it. Dis ...

  9. React源码 ReactElement

    我们的JSX里面标签,属性,内容都会传递到React.createElement()这个方法里面.那么这个方法他到底有什么意义以及他的返回,我们叫他ReactElement.他到底有什么样的作用 /* ...

随机推荐

  1. 【2017集美大学1412软工实践_助教博客】团队作业7——Alpha冲刺之事后诸葛亮

    题目 团队作业7: http://www.cnblogs.com/happyzm/p/6827853.html 团队成绩 评分项目 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 全组 ...

  2. java继承中的初始化顺序

    初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类 ...

  3. Java 第八周总结

    1. 本周学习总结 2. 书面作业 1.List中指定元素的删除 1.1 实验总结 list中可以通过list.get(i)来获取具体第几个的元素的值,再通过compareTo来对比 通过in.has ...

  4. 201521123114 《Java程序设计》第2周学习总结

    #1. 本周学习总结 1. 学会了使用码云管理代码,使用eclipse关联jdk源代码: 2. 学习了包管理机制,包的作用:主要是管理java文件,解决同名文件冲突; 3. ArrayList可以处理 ...

  5. 201521123076 《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 创建线程方式: 定义Thread的子类,覆盖run(),之后创建实例.因为开销大,一般不用. 定义实现Run ...

  6. 201521123114 《Java程序设计》第10周学习总结

    1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 1. 创建线程方式: 定义Thread的子类 定义实现Runnable接口的类,实现run() 2. 调用s ...

  7. Eclipse rap 富客户端开发总结(10) : Rap不同系统间的差异和处理方式

    平常进行 rap 程序开发一般都是在 win 下面完成 , 然后在 tomcat 下面测试 , 但是程序最终发布一般都是在 linux  aix 上面 , 这个时候就有能会出现一下问题,下面 2 个问 ...

  8. python实现裴波那契数列

    def Fib(n): ''' 假定序号为0或者1,返回1,序号为2时返回2 ''' before = 1 after = 1 for i in range(n): before, after = a ...

  9. mybatis 自动生成代码(mybatis generator)

    pom.xml 文件配置 引入 mybatis generator <properties> <mysql.connector.version>5.1.44</mysql ...

  10. Java 异常机制

    Java 异常机制 什么是异常 异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程 为什么要有异常 什么出错了 哪里出错了 ...