React

参照React官网,基本上就是自己直译过来的,因为看着英文很别扭
React官网

Component

组件分离你的UI界面为独立的,可重复利用的模块。各模块相互独立的同时能够相互利用。在某种程度上,组件有点类似JS中的函数,他们接受任意的输入(props)并且返回描述其特性的React元素。

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

  • 你也可以使用ES6的class去定义一个组件
    class Welcome extends React.Component {
    render() {
    return <h1>Hello, {this.props.name}</h1>;
    }
    }

元素可以代表DOM节点,也可以表示用户已定义的组件。

     const element = <div />

    class Welcome extends React.Component {
                render() {
                          return <h1>hello {this.props.name} </h1>
                }
        }
    const element = <Welcome name='mianmian'/>

    ReactDOM.render(
              element,
              document.getElementById('example')
      )
警告:
  • 组件的名字首字母要大写

  • 组件可以在输出时调用其他的组件,通过这样的方式,可以使一些抽象的组件变得具体。

    class Welcome extends React.Component {
                                render() {
                                        return <h1>hello {this.props.name} </h1>
                                      }
                            }
    
    class App extends React.Component {
                              render() {
                                       return (
                                                <div>
                                                <Welcome name='zhuang' />
                                                <Welcome name='weimian' />
                                                </div>
                                            )
                                      }
                              }
    
    const element = <App />
    ReactDOM.render(
                <App />,
                 document.getElementById('example')
                  )
    

提取组件

提取组件出来使得组件可重复利用,而且让代码阅读起来比较简洁,不会那么冗余。

      class Welcome extends React.Component {
                                    render() {
                                       return <h1>hello {this.props.name} </h1>
                                          }
                                      }

      class Old extends React.Component {
                                    render() {
                                            return <h1> {this.props.old} </h1>
                                              }
                                    }

        class App extends React.Component {
                                    render() {
                                         return (
                                              <div>
                                              <Welcome name={this.props.name} />
                                              <Old old={this.props.old} />
                                             </div>
                                          )
                                      }
                                }


        const element = <App />
        ReactDOM.render(
                  <App name='zhuang' old='15'/>,
                 document.getElementById('example')
              )

所有的React组件必须像纯函数一样不改变Props的值

State和Lifecycle

state的特性跟props有点类似,但是它是private和受组件控制的,state只适用于组件通过class方式定义时的情况,所以有时要将函数式组件转换为class式组件

添加本地的state到一个class中
  1. render()方法中用this.state.date代替this.props.date

  2. 添加一个class constructor来初始化this.state

  3. 去掉ReactDOM中的props属性

       class Operation 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>
               );
         }
     }
       const element = <Operation />
    
       ReactDOM.render(
                 element,
                document.getElementById('example')
           )
    

生命周期

在一个组件的应用中,应释放组件里的资源

      componentDidMount () {
        }
      componentWillUnmount () {
        }

这些方法称为生命周期的钩子componentDidMount() 钩子在组件输出在DOM上后执行,比如可以在 componentDidMount() 函数中加入一个计时器,在componentWillUnmount()中销毁。

    componentDidMount() {
          this.timerID = setInterval(() => this.tick(),1000);
      }
    componentWillUnmount() {
          clearInterval(this.timerID);
      }

最后,我们将实现tick()方法每秒,用this.setState()来更新state的值

    class Operation extends React.Component {
            constructor(props) {
            super(props);
            this.state = {date: new Date()};
          }

    componentDidMount() {
            this.timeID = setInterval( () => this.tick(),1000);
          }
    componentWillMount() {
            clearInterval(this.timeID);
          }

    tick() {
        this.setState({date: new Date()})
    }
  render() {
        return (
            <div>
              <h1>hello world</h1>
              <h2>It is {this.state.date.toLocaleTimeString()}</h2>
            </div>
        );
      }
  }
const element = <Operation />

ReactDOM.render(
      element,
     document.getElementById('example')
  )
正确使用state

不要直接改变state的值,要使用setState().

    // Wrong
    this.state.comment = 'Hello';
    // Correct
    this.setState({comment: 'Hello'});

constructor唯一可以分配this.state的地方

state的更新可以是异步的

因为this.propsthis.state更新可以是异步的,所以我们不能通过他们的值来计算下个state

    // Wrong
    this.setState({
        counter: this.state.counter + this.props.increment,
    });

如果一定要计算该怎么办呢?
可以用setState()来接收一个函数而不是一个对象。这个函数接受先前的state作为其第一个参数,当前的props作为第二个参数

  class Operation extends React.Component {
      constructor(props) {
      super(props);
      var date = 0;
      this.state = {date: parseInt(date) + 1};
    }

  componentDidMount() {
          this.timeID = setInterval( () => this.tick(),1000);
    }
  componentWillUnmount() {
          clearInterval(this.timeID);
    }

  tick() {
            this.setState((prevState, props) => ({
            date:parseInt(prevState.date) + parseInt(props.increment)
          }))
    }
  render() {
        return (
            <div>
              <h1>hello world</h1>
              <h2>It is {this.state.date}</h2>
            </div>
          );
      }
}
  const element = <Operation increment='12'/>

    ReactDOM.render(
           element,
           document.getElementById('example')
    )

问题
为什么

  // Wrong
      this.setState({
              counter: this.state.counter + this.props.increment,
      });

这种方式得到是一样的结果

State Update are Merged

当你调用setState()时,React会合并你当前所提供的state对象
你可以在各自调用setState()来更新他们各自的属性。

数据流

不论是父组件还是子组件都不知道某一个组件的状态,他们也没有关心组件定义的方式。因此state经常被称为是本地的或者是封闭的。
一个组件可以选择传递其state特性像props给其子组件:

  <h2>It is {this.state.date.toLocaleTimeString()}.</h2>

这同样可以工作在已定义的组件中:

  <FormattedDate date={this.state.date} />

该组件将会接受一个date在它的props中即使不知道date来自哪里

    function FormattedDate(props) {
        return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
    }

这样的方式通常称之为"由上到下"或者单向数据流。任何state总是被一些特定的组件包含着,任何来源于state的数据或UI只能影响在他们所在的组件内部。

如果你把组件树当作是props的瀑布,每个组件的state就像多出来的水源,来源于任意的地方但以同样的方式流动。

为了展示所有的组件都是独立的,我们可以写一个App组件,来渲染三个<clock>

    function App() {
        return (
            <div>
                <Clock />
                <Clock />
                <Clock />
            </div>
        );
  }

    ReactDOM.render(
          <App />,
          document.getElementById('root')
    );

每个clock开始各自的计时器,各自更新。
React app中,你可以将一个无状态的组件包裹在一个有状态的组件里,反之亦然。

处理事件

React元素处理事件跟在DOM上处理事件很相似,不过有一些语法的区别

  • React事件命名运用的是驼峰法,而不是小写

  • 运用Jsx你通过一个函数作为事件处理器,而不是一个字符串

      <button onclick="activateLasers()">
          Activate Lasers
      </button>
    // slightly different in React:
    
      <button onClick={activateLasers}>
              Activate Lasers
      </button>
    

当你使用React时你一般不需要调用addEventListener添加对DOM元素的监听,而是在最初渲染元素的时候添加一个监听器。

条件渲染
`React`中条件渲染跟`javascript`工作的情况一样,

  class Operation extends React.Component {
        constructor(props) {
          super(props);
        }

  render() {
       const isOk = this.props.isOk;
       if(isOk) {
          return <Add />;
      }
      return <FalseReturn />;
    }
}
class Add extends Component {
    render() {
        return (<h2>ok</h2>) 
      }
  }

class FalseReturn extends Component {
      render() {
          return (<h1>no</h1>)
      }
  }
const element = <Operation isOk='true'/>

ReactDOM.render(
        element,
       document.getElementById('example')
    )

表格

受控组件

html中,表格元素例如<input>,<textarea>,<select>根据输入的值维持自身的state值,在React中,state保持原有组件的state属性,只通过setState()来更新。
我们通过使React state变成单一数据源的形式结合上述两种方式。接着React组件会在渲染表格的同时控制表格中的用户输入。这样,表格元素的值受React控制,因此就叫受控组件
例如,如果我们想要在提交的时候记录名字,我们就可以将一个表格作为一个受控组件:

    class NameForm extends React.Component {
        constructor(props) {
            super(props);
            this.state = {value: ''};

            this.handleChange = this.handleChange.bind(this);
            this.handleSubmit = this.handleSubmit.bind(this);
      }

      handleChange(event) {
          this.setState({value: event.target.value});
      }

      handleSubmit(event) {
          alert('A name was submitted: ' + this.state.value);
          event.preventDefault();
      }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
     <label>
      Name:
    <input type="text" value={this.state.value} onChange={this.handleChange} />
    </label>
    <input type="submit" value="Submit" />
  </form>
    );
  }
}

由于value的属性被安置在我们的表格元素中,展示的value就永远都是this.state.value,这就构成了React的数据源。由于handleChange在每次点击事件更新state时工作,展示的value也会随用户的输入更新。

有了受控组件,每个state变化将会与一个处理器函数联系起来。这样会使得修改数据和是数据生效更加直接。比如,

  handleChange(event) {
      this.setState({value: event.target.value.toUpperCase()});
  }

The textarea Tag

html中,一个<textarea>元素定义其文本是通过其孩子的形式:

  <textarea>
      Hello there, this is some text in a text area
  </textarea>

React中,一个<textarea>使用的是value属性。通过这样的方式,一个表格使用<textarea>跟使用单行输入相似。

  class EssayForm extends React.Component {
      constructor(props) {
          super(props);
          this.state = {
          value: 'Please write an essay about your favorite DOM element.'
      };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
      this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
        Name:
        <textarea value={this.state.value} onChange={this.handleChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
    );
  }
}

The select Tag

html中,<select>创造了一个下拉框,如:

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

在React中,是在根select标签中使用 value,这种方式在受控组件中更方便,因为你只需要在一个地方更新,比如:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
}

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite La Croix flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

总的来说,<input type="text">,<textarea>,<select>工作的原理很相似,都是接受一个value属性去实现一个受控组件。

处理多输入

当你需要处理多输入受控input元素时,你可以添加一个name属性给每一个元素,让处理器函数决定根据event.target.name的值来操作。

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked :target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

Refs和DOM

在一般的数据流中,props是父组件与子组件交流的唯一方式。为了修改子组件,你要用新的props重新渲染一次,尽管如此,这里还有一些其他的情况你需要在数据流之外以命令式的方式改变子组件。这个被修改的孩子可以是React组件中的一个例子,又或者是一个DOM元素,对于这两种情况,React提供了一种方式去获取。

什么情况使用Refs
  • 聚焦,文本框选择,媒体重播
  • 触发命令
  • 与第三方DOM库交流

避免将refs用于任何可以声明的事件,例如,不要暴露open()close()方法在一个Dialog组件中,而是传递一个isOpen属性值给它。

添加一个Ref给一个DOM元素

React支持一个你可以接触到任何组件的特殊属性。ref属性携带一个返回函数.
ref属性运用在HTML元素是,ref回调函数接受一个潜在的DOM元素作为其参数。通过ref回调函数来设置一个class性质对于DOM元素是一种通用的方式。还有一种更好的方式是在ref的回调函数设置属性。

    ref = {input => this.textInput = input}

ref和函数式组件

在没有实例的情况下,你可能会使用ref属性在一个函数式组件中

    function MyFunctionalComponent() {
            return <input />;
        }

    class Parent extends React.Component {
            render() {
          // This will *not* work!
              return (
              <MyFunctionalComponent
            ref={(input) => { this.textInput = input; }} />
            );
          }
    }

如果你需要一个ref你应该转化组件为class形式。就跟当你需要使用生命周期或者state的时候转化组件为class形式一样。
尽管如此,你可以在一个函数式组件中使用ref属性只要你参考DOM元素或者class组件

    function CustomTextInput(props) {
      // textInput must be declared here so the ref callback can refer to it
          let textInput = null;

    function handleClick() {
            textInput.focus();
        }

    return (
          <div>
            <input
                type="text"
                ref={(input) => { textInput = input; }} />
          <input
              type="button"
              value="Focus the text input"
              onClick={handleClick}
            />
      </div>
    );  
}

不要过度使用refs

你会倾向于在应用里使用refs。如果是这种情况,花些时间想一想在组件的层级里state应该在哪里。通常,拥有state合适的地方应该在层级的高处

警告

如果ref回调函数被定义为一个内部函数,在一次更新中他会被调用两次,第一次是null,再一次是DOM元素。你可以通过定义ref回调函数作为一个class的绑定方法,但是大部分情况下无关紧要

非受控组件

在大多数情况下,我们推荐使用受控组件来实现表格。在受控组件中,表格数据受React组件处理。另外一种选择是非受控组件,表格数据受DOM自身处理
在写一个非受控组件时,你不用写一个事件处理器为state的更新,你可以使用refDOM中获取表格数据
例子:

    class NameForm extends React.Component {
                constructor(props) {
                super(props);
                this.handleSubmit = this.handleSubmit.bind(this);
              }

    handleSubmit(event) {
        alert('A name was submitted: ' + this.input.value);
        event.preventDefault();
        }

    render() {
            return (
              <form onSubmit={this.handleSubmit}>
              <label>
                Name:
                <input type="text" ref={(input) => this.input = input} />
            </label>
            <input type="submit" value="Submit" />
          </form>
    );
  }

自从非受控组件保持了DOM的数据源后,会使得受控组件与非受控组件的交流变得容易。这也会使代码变得简洁。除此之外,你应该常常使用受控组件

Default Values

React渲染的生命周期中,form元素中的value属性将会被覆盖在DOM里。有了非受控组件,你可以使用React去初始化一个值,但是让随后的更新不受控制,为了处理这种情况,你可以特征化一个defaultValue属性而不是value属性。

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
            <label>
              Name:
              <input
                defaultValue="Bob"
                type="text"
                ref={(input) => this.input = input} />
          </label>
            <input type="submit" value="Submit" />
      </form>
      );
  }

像这样,<input type='checkbox'><input type='radio'>支持defaultChecked,<select>支持defaultValue

容器

一些组件不能提前知道他们的孩子,我们推荐使用一些特殊的children属性直接输出子元素

    function FancyBorder(props) {
        return (
          <div className={'FancyBorder FancyBorder-' + props.color}>
          {props.children}
        </div>
    );
  }

这样让其他的组件通过包裹在jsx传递任意的子元素给他们

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

推荐阅读更多精彩内容

  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,770评论 1 18
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,758评论 0 24
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 7,981评论 2 35
  • 本笔记基于React官方文档,当前React版本号为15.4.0。 1. 安装 1.1 尝试 开始之前可以先去co...
    Awey阅读 7,532评论 14 128
  • 渐去的年 葛小青 年就这么过去了?她,轰轰烈烈的,在孩童诸多期许中来,又悄无声息的,在中年添加的细纹...
    葛小青阅读 174评论 0 0