React笔记

关于JSX

考虑这样一段代码:
const element = <h1>Hello, world!</h1>;
这段代码既不是字符串也不是HTML,它是JSX,是javascript的拓展。在React里用来描述UI,因为JSX是用来生产React内的“元素”的。以后会介绍它是如何被渲染成DOM的。

我们可以在JSX里嵌入任何的javascript代码,例如下面这个例子就混合了javascript代码:
function formatName(user) {    return user.firstName + ' ' + user.lastName; } const user = {    firstName: 'Harper',    lastName: 'Perez' }; const element = ( <h1> Hello, {formatName(user)}! </h1>);  ReactDOM.render(    element,    document.getElementById('root')  );
需要指出的是,虽然JSX是javascript的拓展,但是实际到最后,JSX还是要被编译成纯粹的javascript对象,所以在if语句、for循环语句里面都可以用,而且还可以把它当成函数参数或返回值。例如:
function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}!</h1>; } return <h1>Hello, Stranger.</h1>; }
鉴于JSX相比HTML更接近javascript,所以它里面元素的属性采用驼峰命名法,例如class要写成className,tabindex要写成tabIndex。

JSX自带过滤以避免XSS攻击,所以不必担心注入的问题。

元素渲染

元素是React应用里最小的构造块,一个元素所描述的内容决定了你在屏幕上看到的东西。比如这个:
const element = <h1>Hello, world</h1>;
另外,不同于浏览器DOM元素,React中的元素都是纯粹的对象,创建起来代价很低,而且React会负责更新浏览器DOM以便于和React元素相匹配。

React中的元素是不可变的,一旦创建了一个元素,就无法改变它的属性或者是后代元素,这有点类似电影中的一帧,仅是某个时间点的快照。就目前的知识来说,如果想要更新UI,唯一的方法就是再创建一个新的元素,比如我想制作一个显示时间的程序,要做到内容每秒刷新一次,那么以目前的知识来说,只能这么写代码:
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);
值得注意的是,虽然每秒钟我们创建一个新的元素,但React会十分智能的分辨其中的不同,每次仅仅会改变不同的部分,以上面的代码为例,会发现每次只改变了dom中的文字。

变化图
变化图

在React程序中大部分代码都是一次性的,不牵扯到动态刷新的问题,不过在后面会介绍如何利用状态组件解决这个问题。

组件

组件给了你将UI切割的能力,以便更好地复用UI代码。从概念上看,组件和javascript中的函数很像,可以接受任意的输入然后返回一个最终绘制在屏幕上的React元素。

创建组件的最简单方式就是写一个javascript函数:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }

之前的React元素都采用的是原生的dom标签,比如div,其实React提供了一种机制来满足我们采用自定义标签,例如:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
解释React对一下上面这段代码干了什么:

  1. <Welcome name="Sara" />元素调用ReactDOM.render()方法。
  2. React以{name: 'Sara'}为props调用Welcome组件。
  3. Welcome组件返回了一个<h1>Hello, Sara</h1>元素。
  4. React高效的更新DOM。

需要注意的是组件均是以大写字母开头,比如<div/>就是一个dom标签,而<Welcome/>就是一个组件,而且需要在作用于中有一个对应的Welcome函数。

需要指出的是,组件可以嵌套,例如:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );

注意:组件返回的必须是一个元素,例如上面的代码中,虽然希望的是三个Welcome元素,但是必须用一个div包裹起来,不能直接返回三个元素。

鉴于组件的可嵌套能力, React推荐将一个大组件分割成小组件,例如下面这个组件:
function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
现在演示一下如何拆分这个组件,首先,可以提取出Avatar
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
然后提取Comment:
function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
提取工作一开始看起来可能是乏味的,但是在大型应用中,这可以极大的提高代码复用率。一个指导原则是,如果一个UI要素多次出现或者太大,都要做拆分以便复用。

有一点需要说明的是,组件的props都是只读的,不要想在组件中修改props,那样不会起任何作用。

生命周期

思考一下上面演示的时钟程序,到目前为止只介绍了一种更新UI的方法,也就是调用ReactDOM.render()去重新渲染,如下所示:
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);
再介绍新方法前,我们先将这段代码利用React的组件化能力进行封装:
function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);
仅仅做到这样是不够的,我们希望将更改时间的逻辑和元素绑定到一起,去除掉setInterval,最终的理想效果应该这样的:
ReactDOM.render( <Clock />, document.getElementById('root') );
为了到达理想的效果,我们需要为组件添加state。要想使用state就需要讲组件用class的写法,因为class写法在定义class时可以提供一些额外的特性,state就是其中之一。
将函数化的组件转化成class的写法,可以按照下面的步骤进行:

  1. 创建一个同名的ES6 class,该class继承自React.Component
  2. 添加一个空的render方法。
  3. 将函数的主体部分挪到render方法内。
  4. 用this.props替换props。
  5. 将原来的函数形组件删掉。

上述操作完成后函数就转换为了class:
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } }
接下来就能用statelifecycle hooks这两项技术,首先用state代替props
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
然后添加一个构造函数去初始化this.state:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
需要注意的是,构造函数的参数是props并且将这个参数传给了基类。
接下来可以将date(prop)从Clock元素上移除了。
ReactDOM.render( <Clock />, document.getElementById('root') );
此时我们的组件是这样的:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
接下来我们将为Clock设置一个定时器,这需要我们了解React组件生命周期的知识,React中的组件会经历从创建到移除的一个周期,暴露给我们的是两个钩子函数:componentDidMount和componentWillUnmount,分别代表创建和移除,在这两个时间点上我们可以做一些工作。例如定义计时器:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );

需要注意的是,在更新date数值的时候,不能直接赋值,而是采用this.setState方法进行赋值。不过在构造函数里给date初始化的时候可以直接赋值。

事件处理

React里的事件名称采用的是驼峰拼写法,比如HTML中是onclick,在React里就是onClick。另外不能通过返回false来阻止默认事件,而应该用preventDefault方法。

感觉不管是React还是Angular,在工具性上都比较差,可能需要其它附件做为拓展吧,总之工具性的都比较差,常常有不够用的感觉。

有条件的渲染

在React里面,可以根据实际情况有选择性的渲染,例如考虑下面两个组件:
function UserGreeting(props) { return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; }
然后创建一个新的组件,来有对上面两个组件做选择:
function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } ReactDOM.render( // Try changing to isLoggedIn={true}: <Greeting isLoggedIn={false} />, document.getElementById('root') );
React支持将元素变量化以便根据不同的情况渲染不同的元素,如下:
function LoginButton(props) { return ( <button onClick={props.onClick}> Login </button> ); } function LogoutButton(props) { return ( <button onClick={props.onClick}> Logout </button> ); } class LoginControl extends React.Component { constructor(props) { super(props); this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this); this.state = {isLoggedIn: false}; } handleLoginClick() { this.setState({isLoggedIn: true}); } handleLogoutClick() { this.setState({isLoggedIn: false}); } render() { const isLoggedIn = this.state.isLoggedIn; let button = null; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); } } ReactDOM.render( <LoginControl />, document.getElementById('root') );
注意其中button变量的用法。
同时,在组件中还可以用javascript逻辑控制语句,如:
function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( <Mailbox unreadMessages={messages} />, document.getElementById('root') );
React的组件也可以用condition ? true : false语句,如:
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div> ); }
或者更复杂一些的:
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> )} </div> ); }
如果不希望组件渲染出内容,可以返回null;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • react 基本概念解析 react 的组件声明周期 react 高阶组件,context, redux 等高级...
    南航阅读 1,051评论 0 1
  • 最近看了一本关于学习方法论的书,强调了记笔记和坚持的重要性。这几天也刚好在学习React,所以我打算每天坚持一篇R...
    gaoer1938阅读 1,666评论 0 5
  • 本笔记基于React官方文档,当前React版本号为15.4.0。 1. 安装 1.1 尝试 开始之前可以先去co...
    Awey阅读 7,641评论 14 128
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,041评论 2 35
  • 运营自媒体已有一些时日,从刚刚注册头条号开始,到自媒体平台都有自己的文章。一路走来有喜有忧,未来不知道自媒体这条路...
    风华真年少阅读 153评论 0 0