React Native开发之React基础

199 阅读7分钟

# React是什么?

React 是 Facebook 推出的开源 JavaScript Library,React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。React 还可以使用 Node 进行服务器渲染,或使用 React Native 开发原生移动应用。

# 最简易的 React 示例如下:

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

它将在页面上展示一个 “Hello, world!” 的标题。

# 为什么使用 JSX?

JSX 明显更加简洁易读。

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

# 在 JSX 中嵌入表达式

示例中,我们将调用 JavaScript 函数 formatName(user) 的结果,并将结果嵌入到 <h1> 元素中。

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')
);

# 组件 & Props

组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。

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

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

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

提示: React 的 JSX 里约定分别使用首字母大、小写来区分本地组件的类和 HTML 标签。 由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代, React DOM 使用 className 和 htmlFor 来做对应的属性。所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

# State & 生命周期

componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行,所以,最好在这里设置计时器, 一旦 Clock 组件从 DOM 中被移除,React 就会调用 componentWillUnmount() 生命周期方法,这样计时器就停止了。

必须在 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')
);

不要直接修改 State,例如,此代码不会重新渲染组件:

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

如果更新State 需要用到Props 属性,将此次更新被应用时的 props 做为第二个参数:

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

# 元素渲染

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);

遍历渲染 this.props.children会返回组件对象的所有属性。 React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map或React.Children.forEach 来遍历子节点。

React.Children.map(children, function[(thisArg)])
React.Children.forEach(children, function[(thisArg)])

示例

class NotesList extends React.Component{
    render(){
        return (
            <ol>
                {
                    React.Children.map(this.props.children,(child)=> 
                    {
                        return <h1>{child}</h1>;                     
                    })
                }
            </ol> );
    }
}
ReactDOM.render(<NotesList>
    <span>hello</span>     
    <span>world</span> 
    </NotesList>, 
    document.getElementById('root'));

列表可以参考此方式渲染。

# ref属性 获取真是DOM

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。

但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性。

class Alert extends React.Component {
    showAlert(message) {
        alert(`Debug:${message}`);
    }

    render() {
        return null;
    }
}

class MyTitle extends React.Component {
    onClick = () => {
        this.refs.alert.showAlert('MyTitle');
    };

    render() {
        return <div>
            <h1 onClick={this.onClick}>Click me</h1>
            <Alert ref='alert'/>
        </div>;
    }
}

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

# 使用 PropTypes 进行类型检查

自 React v15.5 起,React.PropTypes 已移入另一个包中。请使用 prop-types 库 代替。

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

# 组件的生命周期

# 组件的生命周期分成三个时期

  • Mounting:挂载时
  • Updating:更新时
  • Unmounting:卸载时

# Mounting:挂载时

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

  • constructor()

    React组件的构造函数将会在装配之前被调用。当为一个React.Component子类定义构造函数时,你应该在任何其他的表达式之前调用super(props)。否则,this.props在构造函数中将是未定义,并可能引发异常。

  • static getDerivedStateFromProps() 组件实例化后和接受新属性时将会调用getDerivedStateFromProps。它应该返回一个对象来更新状态,或者返回null来表明新属性不需要更新任何状态。

    注意,如果父组件导致了组件的重新渲染,即使属性没有更新,这一方法也会被调用。如果你只想处理变化,那么可以通过比较新旧值来完成。

    调用this.setState() 通常不会触发 getDerivedStateFromProps()。

  • render() render() 方法是 class 组件中唯一必须实现的方法。 当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一: React 元素。通常通过 JSX 创建。例如,

    会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义组件,无论是
    还是 均为 React 元素。 数组或 fragments。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。 Portals。可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档 字符串或数值类型。它们在 DOM 中会被渲染为文本节点 布尔类型或 null。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。) render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

    如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易思考。

  • componentDidMount() componentDidMount()在组件被装配后立即调用,通常在该方法中进行一些初始化操作。初始化时需要DOM节点的操作可以放到这里进行`。若你需要从远端加载数据,这是一个适合实现网络请求的地方。在该方法里设置状态将会触发重渲。

    这一方法是一个发起任何订阅的好地方。如果你这么做了,别忘了在componentWillUnmount()退订。

    另外,在这个方法中调用setState()将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使render()将会调用两次,但用户不会看到中间状态。

# Updating:更新时

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

  • shouldComponentUpdate() 在接收到新的 props 或者 state,将要渲染之前调用,以让React知道当前状态或属性的改变是否不影响组件的输出。

    该方法在初始化渲染的时候不会调用,在使用 forceUpdate 方法的时候也不会。如果确定新的 props 和 state 不需要重新渲染,则此处应该 返回 false。

  • getSnapshotBeforeUpdate() getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。

  • componentDidUpdate() 当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)

    componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。

# Mounting:挂载时

当组件从 DOM 中移除时会调用如下方法:

  • componentWillUnmount() 在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。

    componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

# 不安全的方法

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate 使用这些生命周期方法通常会导致错误和不一致,因此将来会被弃用。在新的React版本中他们被标记为UNSAFE。