React文档笔记

jsx

const element =

Hello, world!

;
这就是 JSX ,他是 JavaScrip 的一种扩展语法。我们推荐在 React 中使用这种语法来描述 UI 信息。JSX 可能会让你想起某种模板语言,但是它具有 JavaScrip 的全部能力。

JSX 表示对象

  • Babel 将JSX编译成 React.createElement() 调用。
  • 下面的两个例子是是完全相同的:
const element = (
  

Hello, world!

);
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

React 只更新必需要更新的部分

React DOM 会将元素及其子元素与之前版本逐一对比, 并只对有必要更新的 DOM 进行更新, 以达到 DOM 所需的状态。

组件(Components) 和 属性(Props)

愚人码头注:Props , 即属性(Property), 在代码中写作 props , 故可用 props 指代 properties .

    1. 函数式组件
    1. 类组件

警告:
组件名称总是以大写字母开始。
举例来说,

代表一个 DOM 标签,而 则代表一个组件,并且需要在作用域中有一个 Welcome 组件。

提取组件

  • 不要害怕把一个组件分为多个更小的组件。
  • 组件化思维

Props 是只读的

  • “纯函数”
    因为它们不会试图改变它们的输入,并且对于同样的输入,始终可以得到相同的结果。
  • 所有 React 组件都必须是纯函数,并禁止修改其自身 props 。

状态(State) 和 生命周期

example:

function Clock(props) {
  return (
    

Hello, world!

It is {props.date.toLocaleTimeString()}.

); } function tick() { ReactDOM.render( , document.getElementById('root') ); } setInterval(tick, 1000);
使用state改进

state 和 props 类似,但是它是私有的,并且由组件本身完全控制。

  • 把函数式组件转化为类组件:
  1. 创建一个继承自 React.Component 类的 ES6 class 同名类。
  2. 添加一个名为 render() 的空方法。
  3. 把原函数中的所有内容移至 render() 中。
  4. render() 方法中使用 this.props 替代 props
  5. 删除保留的空函数声明。
class Clock extends React.Component {
  render() {
    return (
      

Hello, world!

It is {this.props.date.toLocaleTimeString()}.

); } }
  • 在类组件中添加本地状态(state)
    引进 类构造函数(class constructor) 初始化 this.state:
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } ReactDOM.render( , document.getElementById('root') );
  • 在类中添加生命周期方法
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 (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } ReactDOM.render( , document.getElementById('root') );

-组件调用过程

  1. 被传入 ReactDOM.render()时, React 会调用 Clock组件的构造函数。 因为Clock要显示的是当前时间,所以它将使用包含当前时间的对象来初始化this.state。我们稍后会更新此状态。

2.然后 React调用了 Clock 组件的 render()方法。 React 从该方法返回内容中得到要显示在屏幕上的内容。然后,React然后更新DOM以匹配 Clock的渲染输出。

3.当Clock 输出被插入到DOM中时,React 调用 componentDidMount()生命周期钩子。在该方法中,Clock组件请求浏览器设置一个定时器来一次调用 tick()

4.浏览器会每隔一秒调用一次 tick()方法。在该方法中, Clock 组件通过 setState()方法并传递一个包含当前时间的对象来安排一个UI的更新。通过 setState(), React 得知了组件 state(状态)的变化, 随即再次调用render()方法,获取了当前应该显示的内容。 这次,render() 方法中的this.state.date的值已经发生了改变, 从而,其输出的内容也随之改变。React于是据此对DOM 进行更新。

5.如果通过其他操作将 Clock 组件从 DOM中移除了, React会调用 componentWillUnmount()生命周期钩子, 所以计时器也会被停止。

正确地使用 State(状态)

    1. 不要直接修改 state(状态)
// 错误
this.state.comment = 'Hello';
// 正确
this.setState({comment: 'Hello'});
    1. state(状态) 更新可能是异步的
      React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。
      因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。
例如, 以下代码可能导致 counter(计数器)更新失败:
// 错误
this.setState({
  counter: this.state.counter + this.props.increment,
});

要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。这个函数将接收前一个状态作为第一个参数,应用更新时的 props 作为第二个参数:

// 正确
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

state(状态)更新会被合并

当你调用 setState(), React 将合并你提供的对象到当前的状态中。

 componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

合并是浅合并,所以 this.setState({comments}) 不会改变 this.state.posts 的值,但会完全替换this.state.comments 的值。

数据向下流动

如果把组件树想像为 props(属性) 的瀑布,所有组件的 state(状态) 就如同一个额外的水源汇入主流,且只能随着主流的方向向下流动。


处理事件

  • 取消点击默认行为

React 中你不能通过返回 false(愚人码头注:即 return false; 语句) 来阻止默认行为。必须明确调用 preventDefault

function ActionLink() {
  function handleClick(e) {
    // e 无兼容性问题
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    
      Click me
    
  );
}

this的绑定及事件参数传递



述两行代码是等价的,分别使用 arrow functions 和 Function.prototype.bind


条件渲染

  • 有状态组件
    它将渲染 或者 ,取决于当前状态。同时渲染 组件:
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 = ;
    } else {
      button = ;
    }

    return (
      
{button}
); } } ReactDOM.render( , document.getElementById('root') );

使用逻辑 && 操作符的内联 if 用法

您可以 在JSX中嵌入任何表达式 ,方法是将其包裹在花括号中。这也包括 JavaScript 逻辑 && 运算符。

使用条件操作符的内联 If-Else

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    
{isLoggedIn ? ( ) : ( )}
); }

防止组件渲染

  • 在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回 null 而不是其渲染输出。
function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    
Warning!
); } class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true} this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(prevState => ({ showWarning: !prevState.showWarning })); } render() { return (
); } } ReactDOM.render( , document.getElementById('root') );

列表(Lists) 和 键(Keys)

  • 可以创建元素集合,并用一对大括号 {} 在 JSX 中直接将其引用即可。
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  
  • {number}
  • );

    example:

    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        
  • {number}
  • ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('root') );
    • 当运行上述代码的时候,将会收到一个警告:a key should be provided for list items(应该为列表元素提供一个键)(愚人码头注 :CodeOpen 中没有报警告,是因为其示例中使用的是 min 版本的 React,换成非 min 版本的就可以看到)。

    • 解决丢失 key 的问题。

    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        
  • {number}
  • ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('root') );

    键(Keys)

    • 键(Keys) 帮助 React 标识哪个项被修改、添加或者移除了。数组中的每一个元素都应该有一个唯一不变的键(Keys)来标识,
    • 挑选 key 最好的方式是使用一个在它的同辈元素中不重复的标识字符串。多数情况你可以使用数据中的 IDs 作为 keys:
    const todoItems = todos.map((todo) =>
      
  • {todo.text}
  • );
    • 当要渲染的列表项中没有稳定的 IDs 时,你可以使用数据项的索引值作为 key 的最后选择:
    const todoItems = todos.map((todo, index) =>
      // Only do this if items have no stable IDs
      
  • {todo.text}
  • );

    如果列表项可能被重新排序时,我们不建议使用索引作为 keys,因为这导致一定的性能问题,会很慢。

    使用 keys 提取组件

    • 错误的 key 用法
    • 一个好的经验准则是元素中调用 map() 需要 keys 。
    function ListItem(props) {
      // 正确!这里不需要指定 key :
      return 
  • {props.value}
  • ; } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 正确!key 应该在这里被指定 ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('root') );

    keys 在同辈元素中必须是唯一的

    • 在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。
    • 键是React的一个内部映射,但其不会传递给组件的内部。如果你需要在组件中使用相同的值,可以明确使用一个不同名字的 prop 传入。
    const content = posts.map((post) =>
      
    );
    

    上面的例子中, Post 组件可以读取 props.id,但是不能读取 props.key 。

    在 JSX 中嵌入 map()

    • 在上面的例子中,我们单独声明了一个 listItems 变量,并在 JSX 中引用了该变量:
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        
    
      );
      return (
        
      {listItems}
    ); } // 或者jsx使用大括号 function NumberList(props) { const numbers = props.numbers; return (
      {numbers.map((number) => )}
    ); }

    表单(Forms)

    • select 标签

    在 HTML 中,

    注意,Coconut 选项是初始化选中的,因为它的 selected 属性。React 中,并不使用这个 selected 属性,而是在根 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 (
          
    ); } }
    • 总的来说,这使