React文档总结

元素渲染

更新元素渲染

计时器例子

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)

react只会更新必要的部分,上面计时器的例子中,只会更新时间,而element中的Hello, World等则不会更新。

组建 & Props

自定义组件

自定义组件包括函数定义组件和类定义组件,下面例子中两种定义的效果是相同的。

// 函数定义组件
// function Welcome(props){
//   return <h1>Hello, {props.name}</h1>;
// }

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

const element = <Welcome name="Sarah" />;

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

组合组件

组件可以在返回值中引用其它组件,从而实现组件之间的组合。

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

class App extends React.Component {
  render(){
    return (
      <div>
        <Welcome name='Sarah' />
        <Welcome name='Lily' />
        <Welcome name='Dency' />
      </div>
    );
  }
}

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

提取组件

通过提取组件可以将一个大的组件拆分成由许多小的组件合并而成。

原始组件:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avater"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-data">
        {formatData(props.date)}
      </div>
    </div>
  );
}

拆分后:

// Avater组件
function Avater(props) {
  return (
    <img className="Avater" 
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}
// UserInfo组件
function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avater user={props.author} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}
// 修改后的Comment
function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo author={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-data">
        {formatData(props.date)}
      </div>
    </div>
  );
}

Props只能用来读取,不能被修改。

State & 生命周期

生命周期

用组件的生命周期改写上面的计时器功能

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

  // 组件插入到DOM中启动定时器
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  // 组件移出DOM时去除定时器
  componentWillMount() {
    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')
);

正确使用状态

不可直接更新状态

以下代码不会重新渲染组件

this.state.comment = "Hello"

应当使用setState

状态更新可能是异步的

React可以将多个setState()调用合并成一个调用来提高性能。所以状态更新是异步的,不能根据上一个状态值来计算下一个状态值。

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

事件处理

React事件绑定属性命名采用驼峰写法,需要传入一个事件处理函数,而不是一个字符串。

// HTML
<button onclick="activateLasers()">
	Activate Lasers
</button>
// React
<button onClick={activateLasers}>
	Activate Lasers
</button>

在React中不能使用返回false的方式阻止默认行为,而要使用preventDefault。

// HTML
<a href="#" onclick="console.log('The link was clicked.'); return false">
	Click me
</a>
function ActionLink() {
function handleClick(e) {
	e.preventDefault();
	console.log('The link was clicked.');
}
return (
	<a href="#" onClick={handleClick}>
		Click me
	</a>
);
}

使用this绑定点击事件

class Toggle extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      isToggleOn: true
    };
    // This blinding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render(){
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

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

使用如下两种写法可以不用使用bind绑定this

  handleClick = () => {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }
  render(){
    return (
      <button onClick={(e) => this.handleClick(e)}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }

向事件处理程序传递参数

有两种方式可以向事件处理程序传递参数

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deletdRow.bind(this, id)}>Delete Row</button>

下面例子使用bind()传递参数

class Popper extends React.Component{
  constructor(){
    super();
    this.state = {name:'Hello world'};
  }

  preventPop(name, e){  // 事件对象e要放在最后
    e.preventDefault();
    alert(name);
  }

  render(){
    return (
      <div>
        <p>hello</p>
        {/* Pass params via bind() method. */}
        <a href="http://reactjs.org" onClick={this.preventPop.bind(this, this.state.name)}>Click</a>
      </div>
    );
  }
}

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

条件渲染

与运算符 &&

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread message.
        </h2>
      }
    </div>
  );
}

const message = ['React', 'Re: React', 'Re:Re: React']

ReactDOM.render(
  <Mailbox unreadMessages={message} />, 
  document.getElementById('root')
);

在JavaScript中,true && expression 总是返回expression,而 false && expression 总是返回false。

阻止组件渲染

function WarningBanner(props) {
  if(!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

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 (
      <div>
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? 'Hide' : 'Show'}
        </button>
      </div>
    );
  }
}

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

列表 & Keys

渲染多个组件

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => 
  <li>{number}</li>
);

ReactDOM.render(
  <ul>{listItems}</ul>, 
  document.getElementById('root')
);

基础列表组件

function NumberList(props) {
  const number = props.numbers;
  const listItems = numbers.map((number) => 
  <li key={number.toString()}>
    {number}
  </li>
);
return (
  <ul>{listItems}</ul>
);
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

注意,如果不加key属性,会得到一个警告 a key should be provided for list items。意思是每个列表元素都应该分配一个确定的标识。

Keys

一个元素的key最好是这个元素在列表中拥有的独一无二的字符串。通常我们使用来自数据的id作为元素的key。

const todoItems = todos.map((todo) => 
	<li key={todo.id}>
		{todo.text}
	</li>
);

当元素没有确定的id时,可以使用它的序列号索引index作为key

const todoItems = todos.map((todo, index) => 
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);

元素的key在他的兄弟元素之间应该唯一,但是不需要全局唯一,也就是说不同列表的元素可以使用相同的key。
key会作为React的提示,但是不会传递给组件。如果组件中需要使用和key相同的值,可将其作为属性传递:

// Post组件可以读出props.id, 但是不能读出props.key
const content = posts.map((post) => 
	<Post
		key={post.id}
		id={post.id}
		title={post.title}
	 />
);

表单

select

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

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

在React中并不使用之前的selected属性,而是在根select标签上用value来表示选中项。

多个输入

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

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

上面通过给元素一个name属性,让函数根据event.target.name的值来选择做什么,这样就可以在一个处理函数中处理多个input元素。

状态提升

状态提升是指多个组件共用一个值时,每次改变值时不进行双向绑定,而是将该值提升到上一级的共同组件中。

组合 VS 继承

包含关系

使用children属性将子元素直接传递到输出。

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

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

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

除此之外,也可以使用自己约定的属性,在组件中需要多个入口的情况下非常有用。

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane left={<Contacts />} right={<Chat />} />
  );
}

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

特殊实例

一个组件可以是另一个组件的实例。

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

function Dialog(props) {
  return(
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        {props.title}
      </h1>
      <p className="Dialog-message">
        {props.message}
      </p>
    </FancyBorder>
  );
}

function WelcomeDialog() {
  return (
    <Dialog
      title="Welcome"
      message="Thakn you for visiting our spacecraft!"
    />
  );
}

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

组合对于定义类的组件同样适用。

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

function Dialog(props) {
  return(
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        {props.title}
      </h1>
      <p className="Dialog-message">
        {props.message}
      </p>
    </FancyBorder>
  );
}

class SignUpDialog extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleSignUp = this.handleSignUp.bind(this);
    this.state = {login: ''};
  }

  render() {
    return (
      <Dialog title="Mars Exploration Program"
              message="How should we refer to you?">
        <input value={this.state.login} onChange={this.handleChange} />
        <button onClick={this.handleSignUp}>
          Sign Me Up!
        </button>
      </Dialog>
    );
  }

  handleChange(e) {
    this,this.setState({login: e.target.value});
  }

  handleSignUp() {
    alert(`Welcome aboard, ${this.state.login}!`);
  }
}

ReactDOM.render(
  <SignUpDialog />,
  document.getElementById('root')
);
原文地址:https://www.cnblogs.com/suraer/p/9171625.html