首页 ReactJS 组件
文章
取消

ReactJS 组件

React 提倡组件化的开发方式,每个组件只关心自己部分的逻辑,使得应用更加容易维护和复用。

React 还有一个很大的优势是基于组件的状态更新视图,对于测试非常友好。

数据模型

state

React 每一个组件的实质是状态机(State Machines),在 React 的每一个组件里,通过更新 this.state,再调用 render() 方法进行渲染,React 会自动把最新的状态渲染到网页上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class HelloMessage extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
    this.state = {enable: false};
  }

  handleClick() {
    this.setState({enable: !this.state.enable})
  }

  render() {
    return (
      <div>
        <input type="text" disabled={this.state.enable} />
        <button onClick={this.handleClick}>click this</button>
      </div>
    );
  }
}

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

通过在组件的 constructor 中给 this.state 赋值,来设置 state 的初始值,每当 state 的值发生变化, React 重新渲染页面。

注意:

(1) 请不要直接编辑 this.state,因为这样会导致页面不重新渲染

1
2
// Wrong
this.state.comment = 'Hello';

使用 this.setState() 方法来改变它的值

1
2
// Correct
this.setState({comment: 'Hello'});

(2) this.state 的更新可能是异步的(this.props 也是如此)

React 可能会批量地调用 this.setState() 方法,this.state 和 this.props 也可能会异步地更新,所以你不能依赖它们目前的值去计算它们下一个状态。

比如下面更新计数器的方法会失败:

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

第二种形式的 setState() 方法接收的参数为一个函数而不是一个对象。函数的第一个参数为 previous state,第二个参数为当前的 props

1
2
3
4
// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

实现一个计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class HelloMessage extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = {counter: 0};
  }

  handleClick() {
    this.setState((prevState, props) => ({
      counter: prevState.counter + parseInt(props.increment)
    }));
  }

  render() {
    return (
      <div>
        <h1>{this.state.counter}</h1>
        <button onClick={this.handleClick}>click this</button>
      </div>
    );
  }
}
ReactDOM.render(
    <HelloMessage increment="1" />,
    document.getElementById('root')
);

props

React 的数据流是单向的,是自上向下的层级传递的,props 可以对固定的数据进行传递。

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

state vs props

state 和 props 看起来很相似,其实是完全不同的东西。

一般来说,this.props 表示那些一旦定义,就不再改变的特性,比如购物车里的商品名称、价格,而 this.state 是会随着用户互动而产生变化的特性,比如用户购买商品的个数。

获取 DOM

在 React 中,我们可以通过 this.refs 方便地获取 DOM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class HelloMessage extends React.Component {
  constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    alert(this.refs.myInput.value);
  }

  render() {
    return (
      <div>
        <input ref="myInput" />
        <button onClick={this.handleClick}>click this</button>
      </div>
    );
  }
}

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

生命周期

React 组件的生命周期分为三类:

1. 挂载(Mounting): 已插入真实 DOM

  • componentWillMount(): 在初次渲染之前执行一次,最早的执行点
  • componentDidMount(): 在初次渲染之后执行

getInitialState() –> componentWillMount() –> render() –> componentDidMount()

2. 更新(Updating): 正在被重新渲染

  • componentWillReceiveProps(): 在组件接收到新的 props 的时候调用。在初始化渲染的时候,该方法不会调用。
  • shouldComponentUpdate(): 在接收到新的 props 或者 state,将要渲染之前调用。
  • componentWillUpdate(): 在接收到新的 props 或者 state 之前立刻调用。
  • componentDidUpdate(): 在组件的更新已经同步到 DOM 中之后立刻被调用。

componentWillReceiveProps() –> shouldComponentUpdate() –> componentWillUpdate –> render() –> componentDidUpdate()

3. 移除(Unmounting): 已移出真实 DOM

  • componentWillUnmount(): 在组件从 DOM 中移除的时候立刻被调用。

下面举 React 官网的一个输出时间的例子,在 Clock 渲染之前设置一个定时器,每隔一秒更新一下 this.state.date 的值,并在组件移除的时候清除定时器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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')
);

事件

React 内建的跨浏览器的事件系统,我们可以在组件里添加属性来绑定事件和相应的处理函数。这种事件绑定方法极大的方便了事件操作,不用再像以前先定位到 DOM 节点,再通过 addEventListener 绑定事件,还要用 removeEventListener 解绑。当组件注销时,React 会自动帮我们解绑事件。

React 处理事件与 DOM 处理事件非常相似,有以下两点不同:

  • React 事件用驼峰命名法,而不是全小写
  • 通过 JSX 语法传递函数作为事件处理器,而不是字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
class LoggingButton extends React.Component {
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

另外一个不同的是 React 不支持向事件处理函数 return false,一般 HTML 事件函数中,可以通过 return false 来阻止默认行为,比如

1
2
3
<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

Vue 阻止浏览器默认行为的方式最简单,用一个装饰符就可以搞定 <form v-on:submit.prevent="onSubmit"></form>。

而在 React 中,必须调用 preventDefault 方法才能完成以上功能。

1
2
3
4
5
6
7
8
9
10
11
12
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

在这里的 e 是 React 封装过后的,因此不用担心游览器差异带来的影响。☺

条件渲染

假设 Greeting 组件根据状态选择渲染 UserGreeting 和 GuestGreeting 中的一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 />;
}

class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }
  handleLogoutClick() {
    this.setState({isLoggedIn: !this.state.isLoggedIn});
  }

  render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLogoutClick} />;
    }

    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }
}

行内条件判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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')
);

其它类型的逻辑判断,像三元运算符,if else React 也均支持。

1
2
3
4
5
6
7
8
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}

阻止组件渲染 通过在组件内部 return null 可以达到阻止组件渲染的

1
2
3
4
5
6
7
8
9
10
11
function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}
本文由作者按照 CC BY 4.0 进行授权

10 分钟快速入门 Python3

PHP-FPM进程管理