Your Site Title

React

Introducing JSX

JSX语法是JavaScript的扩展, 有JS的全部功能, 用来定义UI JSX更接近JS, 不是html, 所以是使用camelCase命名 JSX在渲染前会把所有的值转换成字符串, 可以避免注入和XSS

包含渲染逻辑问题和与其他UI的逻辑的问题:

  1. 怎么处理事件
  2. 状态如何随时间变化
  3. 如何状态显示数据

React没有分离标记与逻辑, 使用组件来组织标记与逻辑

// 可以使用花括号来插入JS表达式
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

// JSX 也是一个表达式
function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

// JSX 可以指定属性 
const element = <a href="https://www.reactjs.org"> link </a>;
const element = <img src={user.avatarUrl}></img>;

// JSX 可以嵌套
const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

// JSX createElement
// Babel 将 JSX编译成 React.createElement()
// 下面两个例子相等
const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

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

JSX 渲染

element是react中最小的单元. 不像浏览器DOM元素, react元素创建开销小, 组件是使用元素组成 react元素是不可变的, 更新元素时需要创建新的元素, 重新渲染. 重新渲染react也会只渲染改变 了的元素

渲染react需要rootDOM, root DOM是html一个容器, 在React Application中一般只有一个根节点, 如果是集成react, 那么可以有任意个.

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element)

组件

组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。本指南旨在介绍组件的相关理念。 // 函数组件 function Welcome(props) { return <h1>Hello, {props.name}</h1>; }

// class组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
} > 二种组件等效, React元素可以是DOM标签(小字字母开头), 也可以是自定义组件(大写字母开头) > JSX收到的属性及子组件, 会打包成props参数传入组件中,  > 所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

const element = <div />;
const element = <Welcome name="Sara" />;

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render(element);

React 应用顶层一般为App组件

状态

class组件才能使用状态, this.state 属性为状态, 当调用this.setState()函数时更新组件, 每次更新时 , class的render方法会被调用, 但是只要在相同的DOM节点中渲染, 就仅有一个组件对象被创建使用.

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

  // 3
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  // 4
  componentWillUnmount() {
    clearInterval(this.timerID);
  } tick() {
    this.setState({
      date: new Date()
    });
  }

  // 2
  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 的更新可能是异步的

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

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

State 的更新会被合并

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

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

数据是向下流动的

组件可以选择把它的 state 作为 props 向下传递到它的子组件中

事件处理

阻止默认行为

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

class组件中事件

你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。 如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 为了在回调中使用 `this`,这个绑定是必不可少的
    this.handleClick = this.handleClick.bind(this);
  }

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

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

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

// public class fields 语法, 实验性质
class LoggingButton extends React.Component {
  // 此语法确保 `handleClick` 内的 `this` 已被绑定。
  // 注意: 这是 *实验性* 语法。
  handleClick = () => {
    console.log('this is:', this);
  }

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

// 箭头函数, 如果该函数作为prop传入子组件, 会造成额外的重新渲染
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 此语法确保 `handleClick` 内的 `this` 已被绑定。
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

向事件处理程序传递参数

// 事件对象e显式传递
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
// 事件对象e隐式传递
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

条件渲染

if 语句

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;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

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

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

与运算符 &&

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

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

三目运算符

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn
        ? <LogoutButton onClick={this.handleLogoutClick} />
        : <LoginButton onClick={this.handleLoginClick} />
      }
    </div>
  );
}

阻止组件渲染

render方法返回null, 该方法不会影响组件生命周期

列表 & Key

渲染多个组件

创建多个组件时, 需要传递key值, key值帮助react识别出哪些元素改变了. 一般使用id值, 列表中唯一. 万不得已时使用下标

function NumberList(props) {
  const numbers = 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提取组件

元素的 key 只有放在就近的数组上下文中才有意义。 key 只是在兄弟节点之间必须唯一

function ListItem(props) {
  // 正确!这里不需要指定 key:
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 正确!key 应该在数组的上下文中被指定
    <ListItem key={number.toString()}              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

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

key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值:

const content = posts.map((post) =>
  <Post
    key={post.id}
    id={post.id}
    title={post.title} />
);

表单

受控组件

在 HTML 中,表单元素(如

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('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

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

textarea, select 使用value属性

非受控组件

Formik

状态提升

在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”

子组件的state值, 父组件通过props传入, props是只读的, 还需要传入onChange函数 , 子组件通过onChange通知父组件调用this.setState()函数.

组合 vs 继承

包含关系

特例关系

React 哲学

DRY: Don’t Repeat Yourself React 中的数据流是单向的,并顺着组件层级从上往下传递

Reference

React
React CN