如何安卓查微信删除的微信聊天记录删除还原

你正在使用的浏览器版本过低,将不能正常浏览和使用知乎。ReactJS(1)
1 组件和组件属性(Component and Props)
1.1 组件定义方式
React 基于组件开发,而组件可以使UI进行分离,并且高可复用性,从概念上来说,更像是 js 的函数,往组件传入参数(也就是 this.props)然后返回整个组件对象,通过 ReactDOM.render 渲染到网页中。
组件定义方式应该有三种:
// test.html
id="root"&&
type="text/babel"&
function Welcome( props ) {
return (&Hello React.js, {props.name} /&&);
ReactDOM.render( &Welcome name="Welcome" /&, document.getELementById('root') );
最终会输出结果:‘Hello React.js, Welcome’
上面函数的参数 props 也就是组件 render 时候内部的一些属性值集合
这种方式会发现并不需要组件函数中存在 render 方法,这个应该只在 类继承和React.createClass 中才需要。下面看看类继承方式
类继承需要用到 ES6 语法中的 class,通过 <ponent 来创建被继承的组件对象,如下:
类继承方式定义组件:(这里注意, <ponent 是个组件类,不是构造函数,直接使用 {} 即可)
class Welcome extends React.Component {
render() {
return (&h1&Hello React.js, {this.props.name}&br /&&/h1&);
渲染方式都是一样的,通过组件名 Welcome 去渲染即可
ReactDOM.render( &Welcome name="Welcome" /&, document.getELementById('root') );
最终结果和函数式组件定义一样
React.createClass
这里使用的是 React 自己的创建组件类的方法去实现,如果对 es6 不太熟悉的话,这种方式更容易接受点
var Welcome = React.createClass({
render: function () {
return (&h1&Hello React.js, {this.props.name}&br /&&/h1&);
ReactDOM.render( &Welcome name="Welcome" /&, document.getELementById('root') );
其实不管使用上面哪种方式,最后生成的组件在 DOM 树中呈现的结果都是一致的,如下:
&h1 data-reactid=".1"&
&span data-reactid=".1.0"&Hello React.js, &/span&
&span data-reactid=".1.1"&Welcome&/span&
&br data-reactid=".1.2"&
还有需要注意的是,不论上面那种方式,Welcome 组件名的首字母一定要大写,否则会报错,并且报错内容都是一样的:‘Uncaught ReferenceError: Welcome is not define’
我们看到上面的 ReactDOM.render 方法里面是直接传的组件形式,事实上 react 也只是变量,比如:
上面的 &Welcome name="Welcome" /& 可以定义成变量,再传入到渲染函数
const element = &Welcome name="Welcome" /&;
ReactDOM.render( element, document.getElementById( 'root' ) );
结果显示上面方式也是 OK 的,那后面的 id 肯定也是可以通过变量传入的,最后就变成如下:
const element = &Welcome name="Welcome" /&;
const id = document.getElementById( 'root' );
ReactDOM.render( element, id );
这样是不是就清爽许多了……
从官文上有一段注意点:
Always start component names with a capital letter.
For example,
represents a DOM tag, but
represents a component and requires Welcome to be in scope.
这里也说明了为什么组件首字母要大小写原因之一:意思就是说 react 会把消息开头的当作 DOM 标签,而把大写开头的才会当成是个组件名称。
1.2 组件嵌套使用
由于组件一旦定义完成,它就是个以独立的个体形式存在,也就是说它可以被单独渲染到 DOM 树中,同时也可以被其它组件引用,作为其输出,并且支持多次使用
如示例:(不仅被其他组件引用,还能多次重复使用)
var Wrap = React.createClass({
&Welcome name="Lizc" /&
&Welcome name="Fll" /&
&Welcome name="Liwy" /&
const element = &Wrap /&;
= document.getElementById( 'root' );
ReactDOM.render( element, id );
输出结果:
Hello React.js, Lizc
Hello React.js, Fll
Hello React.js, Liwy
1.3 提取组件
官文上有这么一句话:‘Don’t be afraid to split components into smaller components.’
意思就是告诉我们不要害怕将组件分割成更小的组件,也就是说我们可以根据我们的需求尽可能的将内容进行组件化,我们来看下官网的示例程序
// extract-component.html
type="text/babel"&
const rootEle = document.getElementById( 'root' );
var Comment = React.createClass({
render: function () {
className="comment"&
className="user-info"&
className="avatar"
src={this.props.author.avatarUrl}
alt={this.props.author.name}
className="user-info-name"&
作者:{this.props.author.name}
className="comment-text"&
问候语:{this.props.text}
className="comment-date"&
日期:{this.props.date}
var element =
author={{ avatarUrl:'./images/ygr-01.jpg', name: 'lizc' }}
text='hello react.js'
date={new Date().toString()}
ReactDOM.render( element, rootEle );
从上面的 Comment 组件看,其实里面包含四个部分(头像,作者,问候语,日期),本着官文给我们的注解,尽可能的组件化,那么这四个部分也就应该成为独立的组件,下面来实现下这四个组件,而数据这块,依然采用父组件中的属性
这里只是个示范,更多的组件化可以根据自己的需求来进行分割
var Comment = React.createClass({
render: function () {
&div className="comment"&
&AvatarComp author={this.props.author} /&
var AvatarComp = React.createClass({
render: function () {
&div className="user-info"&
&img className="avatar"
src={this.props.author.avatarUrl}
alt={this.props.author.name}
最终运行结果和之前一致,说明头像组件化成功,另外可以发现,我们的 AvatarComp 组件是定义在父组件 Comment 之后的,但是依然可以运行。
作者组件:
&AuthorComp name={this.props.author.name} /&
var AuthorComp = React.createClass({
render: function () {
&div className="user-info-name"&
作者:{this.props.name}
问候语组件
var WelcomeComp = React.createClass({
render: function () {
&div className="comment-text"&
问候语:{this.props.text}
&WelcomeComp text={this.props.text} /&
var DateComp = React.createClass({
render: function () {
&div className="comment-date"&
日期:{this.props.date}
&DateComp date={new Date().toString()} /&
最终完整代码:虽然在代码量上貌似多了点,可是这样依赖每个功能都成为了单独的一个组件,可以在任何地方使用它,而父组件中直接引用子组件,也让父组件更容易维护,修改,并且更加直观,总之好处多多。
&script type="text/babel"&
const rootEle = document.getElementById( 'root' );
var Comment = React.createClass({
render: function () {
&div className="comment"&
&AvatarComp author={this.props.author} /&
&AuthorComp name={this.props.author.name} /&
&WelcomeComp text={this.props.text} /&
&DateComp date={new Date().toString()} /&
var AvatarComp = React.createClass({
render: function () {
&div className="user-info"&
&img className="avatar"
src={this.props.author.avatarUrl}
alt={this.props.author.name}
var AuthorComp = React.createClass({
render: function () {
&div className="user-info-name"&
作者:{this.props.name}
var WelcomeComp = React.createClass({
render: function () {
&div className="comment-text"&
问候语:{this.props.text}
var DateComp = React.createClass({
render: function () {
&div className="comment-date"&
日期:{this.props.date}
var element = &Comment
author={{ avatarUrl:'./images/ygr-01.jpg', name: 'lizc' }}
text='hello react.js'
date={new Date().toString()}
ReactDOM.render( element, rootEle );
2 状态和声明周期(State and LifeCycle)
2.1 状态值
React 的数据传递都是单向传递的,而对于需要变化的值,则是通过 state 对象来控制,比如下面的时钟例子,就是通过每秒更新 state 状态的值去刷新显示结果
这有个时钟计时的组件示例:
// lifecycle-state.html
type="text/babel"&
var rootElement = document.getElementById( 'root' );
function tick() {
const element = (
&Hello world!&
&It is {new Date().toLocaleTimeString()}.&
ReactDOM.render( element, rootElement );
setInterval(tick, 1000);
下午2:10:32.
ES6 类方式
class Tick extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
render() {
&h1&Hello world!&/h1&
&h2&It is {this.state.date.toLocaleTimeString()}.&/h2&
ReactDOM.render( &Tick /&, rootElement );
2.2 生命周期
声明周期函数
componentWillMount
componentDidMount
componentWillUpdate
componentDidUpdate
componentWillUnmount
下面是通过类的方式实现了时钟组件,里面使用到了声明周期中的 componentDidMount 和 componentWillUnmount 加载完成和卸载完成两个周期函数
class Tick extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
console.log( 'constructor got' );
componentDidMount() {
this.timerID = setInterval(
() =& this.tick(),
componentWillUnmount() {
clearInterval( this.timerID );
this.setState({
date: new Date()
render() {
&h1&Hello world!&/h1&
&h2&It is {this.state.date.toLocaleTimeString()}.&/h2&
关于类的使用时候,针对 state 有两点需要注意:
this.state 只能在构造器中去声明
如果想通过修改 this.state 来控制 UI 的显示,只能通过 this.setState 方法,而不能直接给 this.state.attr 进行赋值,直接赋值是无法达到刷新 UI 的效果的
下面是通过 React.createClass 方式实现的代码,稍微有点不同,状态对象的初始化,需要用到 getInitialState 函数
var TickComp = React.createClass({
getInitialState() {
date: new Date()
componentDidMount: function () {
this.timerID = setInterval(
() =& this.tick(),
componentWillUnmount: function () {
clearInterval( this.timerID );
tick: function () {
this.setState({
date: new Date()
render: function () {
&Hello world!&
&It is {this.state.date.toLocaleTimeString()}.&
ReactDOM.render(
/&, rootElement );
this.state 这个保存组件状态的对象,是每个组件私有的对象,在组件外是无法直接使用的,但是可以通过和 this.props 结合使用,比如:将本组件的 this.state 通过属性来传递给子组件,然后子组件就可以通过自己的 this.props 来访问这些状态值
比如:上面的时钟,将显示部分组件化
class DisplayDateComp extends React.Component {
constructor(props) {
super(props);
this.state = {
render() {
return (&h2&It is {this.props.date.toLocaleTimeString()}.&/h2&);
&DisplayDateComp date={this.state.date} /&
时钟组件一点生成,便独立存在,就算重复使用也不会互相影响,来试试创建多个时钟看看
class App extends React.Component {
constructor( props ) {
super( props );
render() {
&TickComp /&
&TickComp /&
&TickComp /&
控制台输出:每个组件有自己的计时器
timerID is 1
timerID is 2
timerID is 3
3 事件处理
React 事件和 DOM 事件不同点:
时间名称,React 采用驼峰式命名(如:onChange),DOM 使用的都是小写(如:onkeydown, onkeypress, onclick)
React 在传递事件处理函数时不能使用字符串形式(比如:onclick="buttonClickHandler()"),只能通过 JSX 语法的函数形式传递(比如:{this.buttonClickHandler});
在 DOM 事件处理中,我们可以通过给处理函数添加 return false;去组织默认行为,但是 React 中只能通过调用 preventDefault 才行。
如果使用的是 ES6 类语法创建组件,在添加事件处理的时候需要注意一点,即 this 作用域问题,每个事件处理程序都要在构造函数里面执行一次绑定 this 操作。
class ButtonToggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
this.handleClick = this.handleClick.bind( this );
handleClick() {
console.log( this );
this.setState(
prevState =& ({ isToggleOn: !prevState.isToggleOn })
render() {
&button onClick={this.handleClick}&{this.state.isToggleOn ? 'ON' : 'OFF'}&/button&
ButtonToggle 组件中,如果不在构造器中添加
this.handleClick = this.handleClick.bind( this );
这一句, 通过打印可知在点击的时候 handleClick 里面的 this 打印出来的是 ‘undefined’,会导致报错。
官文中提供了两种解决方法:
事件函数声明时用:箭头函数定义
handleClick = () =& { console.log( this ); }
这中写法会报错 ‘Uncaught SyntaxError: embedded: Unexpected token (20:14)’,是上面的写法有什么问题?
官文注释中有这么一句 // Warning: this is *experimental* syntax. 不知道是不是目前还不支持在类里面这么使用。
然后尝试了下 把它移到构造器里面去,运行正常
constructor(props) {
super(props);
this.state = {
isToggleOn: true
this.handleClick = (e) =& {
console.log( this );
this.setState(
prevState =& ({ isToggleOn: !prevState.isToggleOn })
// this.handleClick = (e) =& console.log( this )
// this.handleClick = this.handleClick.bind( this );
让其成为该类的成员,说明成员的声明必须要放到构造函数里面去。
在绑定事件时使用箭头函数
onClick={ (e) =& this.handleClick(e) }
这种方式如果是在把事件处理函数传递给子组件的时候,会被执行两次;
针对这个尝试了下面的 例子
class ButtonToggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
componentDidUpdate(prevProps, prevState) {
console.log( 'father update' );
handleClick(e) {
console.log( this );
this.setState(
prevState =& ({ isToggleOn: !prevState.isToggleOn })
render() {
&Button clickHandler={ (e) =& this.handleClick(e) } isToggleOn={this.state.isToggleOn} /&
class Button extends React.Component {
constructor(props) {
super(props);
componentDidUpdate(prevProps, prevState) {
console.log( 'son update' );
render() {
&button onClick={ this.props.clickHandler
}&{this.props.isToggleOn ? 'ON' : 'OFF'}&/button&
ReactDOM.render(
&ButtonToggle /&,
rootElement
&Button clickHandler={ (e) =& this.handleClick(e) } isToggleOn={this.state.isToggleOn} /&
上面通过箭头函数传递下去的时候,this 对象从控制台输出看是指向 ButtonToggle 的。
这是控制台,点击按钮时候的输出:
ButtonToggle {props: Object, context: Object, refs: Object, updater: Object, state: Object…}
son update
father update
用上面的方式去实现,貌似并没有官文中说的被重复渲染,有点不太明白,疑问?????
4 条件渲染(Conditional Rendering)
条件渲染其实就是利用 if, if ... else,? ... : 来根据状态值控制组件是否需要被渲染
前面就不介绍了,直接看个这节综合点的示例(官文中最后一个示例)
点击按钮控制警告信息是否显示
&script type="text/babel"&
var rootElement = document.getElementById( 'root' );
function WarningComp( props ) {
if ( !props.warn ) { return null; }
&div className="warning"&Warning!&/div&
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
isShowWarning: true
this.clickHandle = this.clickHandle.bind( this );
clickHandle( e ) {
this.setState(
prevState =& ({ isShowWarning: !prevState.isShowWarning })
render() {
&WarningComp warn={this.state.isShowWarning} /&
&button onClick={ this.clickHandle }&
{this.state.isShowWarning ? 'Hide' : 'Show'}
ReactDOM.render(
rootElement
上例中通过添加条件 if ( !props.warn ) 来决定是否需要渲染 Warning 组件。
另外,组件直接返回 null 并不会影响组件的声明周期,其实可以这么理解 Warning 组件并非不存在而是该组件中并没有任何需要被渲染到 DOM 树中的内容,因此不需要执行渲染部分,但是该组件还是存在的,既然组件存在那就会存在器声明周期,可能通过在组件内部添加声明周期函数来测试
把上面Warning 组件稍做修改下
class WarningComp extends React.Component {
constructor(props) {
super(props);
componentWillMount() {
console.log( 'Warning component will mount' );
componentDidMount() {
console.log( 'Warning component did mount' );
componentWillUpdate(nextProps, nextState) {
console.log( 'Warning component will update' );
componentDidUpdate(prevProps, prevState) {
console.log( 'Warning component did update' );
componentWillUnmount() {
console.log( 'Warning component will unmount' );
render() {
if ( !this.props.warn ) { return null; }
&div className="warning"&Warning!&/div&
默认显示警告:
打开页面时首先输出,这里组件并不是 return null
Warning component will mount
Warning component did mount
接着按 Hide,发现输出的是 update
Warning component will update
Warning component did update
再按 Show,同样输出的是 update 过程
Warning component will update
Warning component did update
从上面输出说明,首先加载,加载过后,只有更新因为组件一开始就已经被加载了,后面只是更改了 warn 状态值,决定是否需要把需要渲染的 DOM 元素渲染到 DOM 树中
默认不显示警告,
// 页面加载完成时控制台输出
Warning component will mount
Warning component did mount
// 点击 Show 输出
Warning component will update
Warning component did update
// 点击 Hide 输出
Warning component will update
Warning component did update
从上面的测试结果说明一点:组件只要一旦创建它就会存在,并且具备组件该有的生命周期,和 render 里面是否有具体的 DOM 元素返回无关,直接返回 null 也同样会执行生命周期函数。
5 列表和键(Lists and Keys)
列表简单示例:
const numbers = [1, 2, 3, 4, 5, 6];
function NumberListComp( props ) {
const numbers
const listItems = numbers.map(
(number) =& &li&{number}&/li&
&ul&{listItems}&/ul&
上面组件渲染结果:
data-reactid=".0"&
data-reactid=".0.0"&1&
data-reactid=".0.1"&2&
data-reactid=".0.2"&3&
data-reactid=".0.3"&4&
data-reactid=".0.4"&5&
data-reactid=".0.5"&6&
从上面可知在没有添加 key 的情况下生成的 DOM 元素及属性
下面将针对 &li& 元素和 li 组件之上添加 key 做实验,给组件添加 key 是为了让 react 更好的发现哪个元素被修改,添加或删除了。添加的结果会直接表现在 data-reactid 之上,来验证下结果:
直接在 &li& 元素之上添加 key:
function NumberListComp( props ) {
const numbers
const listItems = numbers.map(
(number) =& &li key={number}&{number}&/li&
&ul&{listItems}&/ul&
DOM 结果:
data-reactid=".0"&
data-reactid=".0.$1"&1&
data-reactid=".0.$2"&2&
data-reactid=".0.$3"&3&
data-reactid=".0.$4"&4&
data-reactid=".0.$5"&5&
data-reactid=".0.$6"&6&
从上面结果可知,添加在 &li& 元素上的 key 属性,并没有直接以 key=number 形式出现在实际元素属性列表中,而是改变了 data-reactid 属性的值,并且可以发现
如果不添加 key 时候,data-reactid 的值就是 .0. + id 形式,id 从 0 开始依次递增
如果添加了 key 时候,data-reactid 会在 .0. 后面附加一个 $ 符号再加上我们自己定义的 id 也就是后面的 1 ~ 6;
但是直接将 key 属性添加到 &li& 元素上并不是最好的解决办法,如果将 &li& 作为一个单独的组件来处理,key 应该位于组件之上去添加,其实这个也好理解,&li& 本身是 DOM 元素,既然这样就应该让其保持其原有的特征和属性,更何况组件是需要被复用的,当直接把 key 限定死到 &li& 之上,那当被复用到其他地方的时候 key 值的定义会变得容易混淆,而直接添加到组件之上就简单了,哪里用到组件,赋予该组件具体的属性即可,而不用关系组件里面的具体实现。
下面是将 &li& 单独作为组件分离出来,将 key 添加到组件引用时的属性之上的示例
function ListItem( props ) {
return (&li&{props.value}&/li&);
function List( props ) {
const numbers = props.
const listItems = numbers.map(
(number) =&
&ListItem key={number.toString()}
value={number} /&
&ul&{listItems}&/ul&
DOM 结果:
data-reactid=".0"&
data-reactid=".0.$1"&1&
data-reactid=".0.$2"&2&
data-reactid=".0.$3"&3&
data-reactid=".0.$4"&4&
data-reactid=".0.$5"&5&
data-reactid=".0.$6"&6&
从结果上看和直接添加到 &li& 之上是一样的,但从组件概念上,在引用组件时指定更实用,让组件更通用点,毕竟组件的属性只有在引用时才需要指定。
6 Form 表单
HTML 表单元素和 React 中的表单元素有一点点区别,这是因为表单里面的元素往往都有自己的一些内部状态,或者说有自己的默认行为,比如下面的表单:
action="#"&
type="text" name="name" /&
type="submit" value="Submit" /&
上面的示例中在点击 submit 之后会有默认的表单提交动作,这行为在 react 中也是可行的,但更好的做法是放弃默认行为,而是利用函数去绑定事件,收集到表单中的数据然后进一步处理之后再进行提交。
表单中有写输入类型的元素的状态值改变都是基于用户输入的,而在 React 当中是通过数据驱动 UI 刷新,而状态值被保存在 React 的 this.state 对象当中,这个对象是基于组件的,也就是说是组件内部对象,组件外是无法直接访问的,当状态值发生改变时或者调用 this.setState 设置状态值之后,React 内部会执行刷新 UI ,从而做到数据驱动UI更新。
下面来使用 React 状态值来改造上面的示例,主要有两个修改
添加状态值
绑定事件并阻止默认行为
类方式:(如果使用 React.createClass,需要用到 getInitialState 来设置状态值对象)
&script type="text/babel" &
const rootEle = document.getElementById( 'root' );
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
nameValue: 'lizc'
this.handleSubmit
= this.handleSubmit.bind( this );
this.handleName
= this.handleName.bind( this );
handleSubmit( event ) {
alert( 'submit name: ' + this.state.nameValue );
event.preventDefault();
handleName( event ) {
this.setState({
nameValue: event.target.value
render() {
&form onSubmit={this.handleSubmit}&
&input type="text" value={this.state.nameValue} onChange={this.handleName} /&
&input type="submit" value="Submit" /&
ReactDOM.render(
&NameForm /&,
文本输入框(text 或 textarea)
从上面 React 代码看,输入框的输入事件被绑定到了 handlerName ,而这里面只是修改了 this.state 点击提交,会在 handleSubmit 里面收集到表单数据,并阻止默认行为。
从 value={this.state.nameValue} 这句可知,输入框的输入内容已经被绑定到了状态值中的 nameValue,因此在通过 setState 去改变其值的时候输入框内容也会发生变化,下面添加另一个输入框试试
直接复制一个输入框即可:
type="text" value={this.state.nameValue} onChange={this.handleName} /&
type="text" value={this.state.nameValue} onChange={this.handleName} /&
通过修改任何一个输入框内容另一个输入框内容也会随之发生变化,这是因为两个输入框的 value 都被绑定到了状态值 nameValue ,而此时输入框的变化事件为:handleName,其中又是设置了 nameValue,根据状态值驱动 UI 变化,因此两个输入框内容会同步更新。
文本区 textarea 同 text 元素
选项(select)
在 HTML 元素的表单选项元素中,其状态值的改变是通过 selected 属性值来设置的,哪个被选中哪一项会被赋予 selected 属性,或者 selected=true,而在 react 中只需要在其顶部元素 &select& 标签中添加状态值即可实现切换。
针对示例做修改:
添加选择元素
给选择元素添加 value 属性和 onChange 事件
handleSubmit 中添加数据集合,并打印出来
handleSubmit( event ) {
let datas = {
name: this.state.nameValue,
color: this.state.selectedColor
alert( JSON.stringify( datas ) );
event.preventDefault();
handleSelect( event ) {
this.setState({
selectedColor: event.target.value
this.state = {
nameValue: 'lizc',
selectedColor: 'blue'
render() {
&form onSubmit={this.handleSubmit}&
&input type="text" value={this.state.nameValue} onChange={this.handleName} /&
Select Your Color:
&select value={this.state.selectedColor} onChange={this.handleSelect}&
&option value="red"&red&/option&
&option value="orange"&orange&/option&
&option value="yellow"&yellow&/option&
&option value="green"&green&/option&
&option value="blue"&blue&/option&
&input type="submit" value="Submit" /&
上面示例说明了列表选项中,只需要给 &select& 和 &option& 标签添加 value 属性,然后将 &select&的 value 绑定状态值 this.state.selectedColor ,即可。
另外,官方提供了个绑定表单事件的统一接口的实现思路,其思路就是通过给元素添加 name 属性,而这个 name 属性值与 this.state 中的表单元素对应的状态值要同名,然后在事件处理函数中获取到目标事件的 target.name 以及 target.value 值来统一更新表单中多个元素的值;
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 3
this.handleSubmit
= this.handleSubmit.bind( this );
this.handleInputChange = this.handleInputChange.bind( this );
handleSubmit( event ) {
let datas = {
isGoing: this.state.isGoing,
number: this.state.numberOfGuests
alert( JSON.stringify( datas ) );
event.preventDefault();
handleInputChange( event ) {
const target
const value
= target.type === 'checkbox'
? target.checked
const name
this.setState({
[name] : value
render() {
&form onSubmit={this.handleSubmit}&
type="checkbox"
name="isGoing"
value={this.state.isGoing}
onChange={this.handleInputChange} /&
Number of guests:
&input type="text"
name="numberOfGuests"
checked={this.state.numberOfGuests}
onChange={this.handleInputChange} /&
&input type="submit" value="Submit" /&
在 handleInputChange 中使用了一个 ES6 新语法,上面有简单描述,详情可查看:。
上面的实现方式,是使多个表单元素绑定到同一个事件处理函数中,可避免重复定义事件处理函数,但需要注意的一点就是,用这种方式去实现,需要搭配 name 属性来使用。
最后,表单元素的事件处理,还可以通过 ref 对象和 defaultValue 来实现,这种方式更简单,后面在具体进行介绍。
7 状态值提升(Lifting State Up)
从文档中看,这节有点像是共享状态值的意思,其实也就是类似上面两个 input 中,任一个元素的更新会让另一个元素也随之更新,从示例中只是多添加了一层不同的计算方式。
这个是官网上温度转换的例子:
&script type="text/babel"&
const root = document.getElementById( 'root' );
const scaleNames = {
'f': 'Fahrenheit',
'c': 'Celsius'
function toCelsius( fahrenheit ) {
return (fahrenheit - 32) * 5 / 9;
function toFahrenheit( celsius ) {
return ( celsius * 9 / 5) + 32;
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if ( Number.isNaN(input) ) {
return '';
const output = convert(input);
const rounded = Math.round( output * 1000 ) / 1000;
return rounded.toString();
function BoilingVerdict( props ) {
if ( props.celsius &= 100 ) {
return &p&The water would boil.&/p&;
return &p&The water would not boil.&/p&;
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {
temperature: '',
scale: 'c'
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
handleCelsiusChange( temperature ) {
this.setState({
temperature: temperature,
scale: 'c'
handleFahrenheitChange( temperature ) {
this.setState({
temperature: temperature,
scale: 'f'
render() {
const scale = this.state.
const temperature = this.state.
const celsius = scale === 'f'
? tryConvert( temperature, toFahrenheit )
const fahrenheit = scale === 'c'
? tryConvert( temperature, toCelsius )
&TemperatureInput
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} /&
&TemperatureInput
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} /&
&BoilingVerdict celsius={parseFloat(celsius)} /&
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
handleChange( event ) {
this.props.onTemperatureChange( event.target.value );
render() {
const temperature = this.props.
const scale = this.props.
&fieldset&
&legend&Enter temperature in : {scaleNames[scale]} &/legend&
value={temperature}
onChange={this.handleChange}
&/fieldset&
ReactDOM.render(
&Calculator /&,
下面看下大小写转换的两个输入框,进行对比下(根据官网上是温度转换的原理实现的)
&script type="text/babel"&
const root = document.getElementById( 'root' );
const alphaCases = {
'u': 'upper',
'l': 'lower'
class AlphaInput extends React.Component {
constructor(props) {
super(props);
this.inputChange = this.inputChange.bind(this);
inputChange( event ) {
this.props.onInputChange( event.target.value );
render() {
{alphaCases[this.props.alphaCase]}case:
&input type="text" value={this.props.value} onChange={this.inputChange} /&
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
alphaCase: 'l'
this.lowerCaseChange = this.lowerCaseChange.bind(this);
this.upperCaseChange = this.upperCaseChange.bind(this);
lowerCaseChange( lower ) {
this.setState({
word: lower.toLowerCase(),
alphaCase: 'l'
upperCaseChange( upper ) {
this.setState({
word: upper.toUpperCase(),
alphaCase: 'u'
render() {
const word = this.state.
const alphaCase = this.state.alphaC
console.log( alphaCase )
const lower = alphaCase === 'u' ? word.toLowerCase() :
const upper = alphaCase === 'l' ? word.toUpperCase() :
console.log( lower )
console.log( upper )
&AlphaInput alphaCase='l' value={lower} onInputChange={this.lowerCaseChange} /&
&AlphaInput alphaCase='u' value={upper} onInputChange={this.upperCaseChange} /&
ReactDOM.render( &Input /&, root );
效果如下:
其实最终能同时更新,最基本的原来就是共享了同一个状态值 word ,然后在渲染之前对该值做了对应的转换,变成各自所需要的值,刷新到 UI。
8 组合与继承(Composition and Inheritance)
React 有一个功能强大的组合模型,并且推荐组件与组件之间通过使用组合来复用代码。这节将介绍使用继承时所遇到的问题,以及如何使用组合来避免这些问题。
9 结束示例
在基础篇完成最后,有个用 react 组件化完成的小示例
如图中显示,将价格表分割成几个部分
从图中分割出各组件如下:
顶级组件,容器:ProductFilterTable
搜索框(包含输入框,选项,文字):ProductSearchBar
输入框组件:SearchInput
库存选项组件:StockCheckBox
产品列表(或价格表)组件:ProductPriceTable
产品类表:ProductLists
产品分类标题:ProductRowTitle
产品行:ProductRow
分节目录:
下面根据上面的划分,来进行代码实现
将上面的所有组件都先定义好,如下
class ProductFilterTable extends React.Component {
render() {
&div className="pd-container"&
&ProductSearchBar /&
&ProductPriceTable /&
class ProductSearchBar extends React.Component {
render() {
&div className="pd-search"&
&SearchInput /&
&StockCheckBox /&
class SearchInput extends React.Component {
render() {
className="pd-search-input"
type="search"
class StockCheckBox extends React.Component {
render() {
&form className="pd-checkbox"&
type="checkbox"
&label&Only show products in stock.&/label&
class ProductPriceTable extends React.Component {
render() {
&div className="pd-price-tbl"&
&tr&&th&Name&/th&&th&Price&/th&&/tr&
&ProductLists category="Sporting Goods" /&
&ProductLists category="Electronics" /&
class ProductLists extends React.Component {
render() {
&ProductRowTitle title={this.props.category} /&
class ProductRowTitle extends React.Component {
render() {
&tr&&td colSpan="2" className="pd-title"&{this.props.title}&/td&&/tr&
class ProductRow extends React.Component {
render() {
&tr&&td&{this.props.name}&/td&&td&{this.props.price}&/td&&/tr&
因为涉及到,输入框内容变化需要动态刷新产品表,那么这里就需要让输入框的变化和产品表刷新发生联系,在 React 都是通过状态来驱动 UI 刷新。可是输入框和产品列表都是独立的组件,不可能直接通过一个状态值来传递信息,而其共同点都在容器之下,所以需要在顶级容器中定一个能让输入框和产品表发生联系的状态,如图:
容器组件修改:
class ProductFilterTable extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
stockChecked: false
this.searchTextChange = this.searchTextChange.bind(this);
this.stockCheckboxChange = this.stockCheckboxChange.bind(this);
searchTextChange( searchText ) {
this.setState({
searchText: searchText
stockCheckboxChange( checked ) {
this.setState({
stockChecked: checked
render() {
&div className="pd-container"&
&ProductSearchBar
onSearchTextChange={this.searchTextChange}
onStockCheckboxChange={this.stockCheckboxChange}
stockChecked={this.state.stockChecked}
searchText={this.state.searchText}
&ProductPriceTable
stockChecked={this.state.stockChecked}
searchText={this.state.searchText}
容器组件的修改主要如代码中 4 个地方,绑定输入框和多选框的事件,让输入框和多选框事件能够触发容器组件中的共享状态值发生改变(searchText和 stockChecked),然后把这两个值传递给价格表组件,价格表组件会根据状态值的改变而刷新UI显示,从而筛选出查找的产品信息。
搜索框组件修改
class ProductSearchBar extends React.Component {
render() {
&div className="pd-search"&
&SearchInput
onSearchTextChange={this.props.onSearchTextChange}
searchText={this.props.searchText}
&StockCheckBox
stockChecked={this.props.stockChecked}
onStockCheckboxChange={this.props.onStockCheckboxChange}
搜索框的修改主要就是透传来自容器组件
的输入框和多选框事件处理函数。
输入框组件修改
class SearchInput extends React.Component {
constructor(props) {
super(props);
this.searchInputChange = this.searchInputChange.bind(this);
searchInputChange( e ) {
this.props.onSearchTextChange( e.target.value );
render() {
className="pd-search-input"
type="search"
value={this.props.searchText}
onChange={this.searchInputChange}
输入框组件的修改,主要涉及两点
第一:将输入框的输入事件处理函数 searchInputChange 绑定到容器组件中定义的输入框事件处理函数 this.props.onSearchTextChange,以便修改共享状态值 searchText。
第二:将输入框的 value 值指定为 this.props.searchText 共享状态值,这样输入框的内容就完全与容器组件中的 searchText 状态绑定了。
库存复选框组件修改
class StockCheckBox extends React.Component {
constructor(props) {
super(props);
this.stockChange = this.stockChange.bind(this);
stockChange( e ) {
this.props.onStockCheckboxChange( e.target.checked );
render() {
&form className="pd-checkbox"&
type="checkbox"
style={{verticalAlign: "middle"}}
checked={this.props.stockChecked}
onChange={this.stockChange}
&label style={{verticalAlign: "middle"}}&Only show products in stock.&/label&
对于库存复选框的修改类似搜索输入框,主要也是事件添加和状态值绑定。
PS: 复选框和说明文字中间居中,只要针对两个元素都设置 verticalAlign: 'middle' 即可对齐。
产品价格表
class ProductPriceTable extends React.Component {
render() {
&div className="pd-price-tbl"&
&tr&&th&Name&/th&&th&Price&/th&&/tr&
&ProductLists
category="Sporting Goods"
searchText={this.props.searchText}
stockChecked={this.props.stockChecked}
&ProductLists
category="Electronics"
searchText={this.props.searchText}
stockChecked={this.props.stockChecked}
这个价格表组件也只是个容器,其中涉及修改也只是将一些属性透传给产品分类表组件(ProductLists)
从上面价格表实现看,其中还可以进行优化,因为产品的种类实际肯定不止两种,如果直接使用 ProductLists 写死,不太现实,可以直接在 ProductPriceTable 组件中对数据进行操作,把所有分类获取到,然后统一获取列表数据
修改如下:
class ProductPriceTable extends React.Component {
render() {
const searchText
= this.props.searchT
const stockChecked
= this.props.stockC
const categories
for ( let i = 0, len = products. i & i++ ) {
let category = products[i].
if ( categories.indexOf( category ) === -1 ) {
categories.push( category );
let productListComps = categories.map(function (category, index) {
&ProductLists
key={category + index}
category={category}
searchText={searchText}
stockChecked={stockChecked}
&div className="pd-price-tbl"&
&tr&&th&Name&/th&&th&Price&/th&&/tr&
{productListComps}
经过上面的优化,不管数据中有多少个分类,都不需要手动去添加分类表组件。
class ProductLists extends React.Component {
render() {
const searchText = this.props.searchT
const sportGoods = getCategoryList( this.props.category, searchText, this.props.stockChecked );
const prefix = this.props.
if ( sportGoods.length &= 0 ) {
&ProductRowTitle title={this.props.category} /&
&tr&&td colSpan="2"&no results&/td&&/tr&
const list = sportGoods.map(function (product, index) {
&ProductRow key={prefix + index} name={product.name} price={product.price} /&
&ProductRowTitle title={this.props.category} /&
分类表的职责就是从数据中根据分类名查找出所有的产品,然后渲染到列表中。
分类标题和行
class ProductRow extends React.Component {
render() {
&tr&&td&{this.props.name}&/td&&td&{this.props.price}&/td&&/tr&
class ProductRowTitle extends React.Component {
render() {
&tr&&td colSpan="2" className="pd-title"&{this.props.title}&/td&&/tr&
这两个其实就很简单调了,拿到标题,产品名称和价格显示出来即可。
产品过滤器
function getCategoryList( category_name, product_name, stocked ) {
if ( !category_name ) { return null; }
const isPrice = /^\$/.test(product_name);
let filterProducts =
if ( typeof stocked !== "undefined" && stocked ) {
filterProducts = filterProducts.filter(function (product) {
return product.stocked === true;
const productsFilterByCategory = filterProducts.filter(function (product) {
return product.category === category_
if ( !product_name ) { return productsFilterByC }
return productsFilterByCategory.filter(function
(product) {
if ( isPrice ) { return product.price.indexOf( product_name ) &= 0; }
let productNameLower = product.name.toLowerCase();
let findNameLower = product_name.toLowerCase();
return productNameLower.indexOf( findNameLower ) &= 0;
getCategoryList 这个函数主要负责数据的操作,通过传入的参数,来决定是否需要进行过滤,第二个参数 product_name 就是搜索框中输入的关键字,第三个乃库存复选框。
该函数可实现以下功能:
根据输入的关键字过滤出包含关键字的产品名的产品列表;
如果搜索框输入内容以 $ 美元符号开头,可以根据价格进行过滤;
库存复选框的状态可以决定是否显示已库存的产品;
完整代码地址:
其实以前零零散散的接触过一些 react 的东西,但大多都是逛论坛网站时候接触到的,所以下定决心自己来通过官方文档来系统性的去学习下。
这篇比较长,本来想分章节来记录,可是想想如果分章节,中间容易出现学习断层,加上有些知识点也并不是很深入,毕竟这篇还是属于基础篇,所以索性将基础的东西集中在一起来学习了,花了几天时间今天结束基础篇,通过最后的示例对于 react 的基础知识多少有了点掌握,接下来将会学习并记录 react 高级部分(也就是官文中的 ‘ADVANCED GUIDES’ 部分),进行进一步的深入学习。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:34391次
排名:千里之外
原创:40篇
评论:13条
(3)(3)(8)(5)(2)(2)(5)(3)(11)

我要回帖

更多关于 微信聊天记录删除还原 的文章

 

随机推荐