React - 状态提升

在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。
虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。
如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。

 

React - 表单

在 react 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState() 来更新。

React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。

可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项
<select multiple={true} value={['B', 'C']}>

当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作

在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value,但输入仍可编辑,则可能是你意外地将value 设置为 undefined 或 null。

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 Formik 是不错的选择。

React - 列表 & Key

用 key 提取组件

元素的 key 只有放在就近的数组上下文中才有意义。

比方说,如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

key 只是在兄弟节点之间必须唯一

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

function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
                       <li key={post.id}>
                         {post.title}
                       </li>
                      )}
      </ul>
  );
  const content = props.posts.map((post) =>
                                 <div key={post.id}>
                                    <h3>{post.title}</h3>
                                    <p>{post.content}</p>
                                    </div>
                                 );
  return (
     <div>
       {sidebar}
       <hr />
       {content}
     </div>
  );
}

const posts = [
  {id: 1, title: 'hello world', content: 'welcome to learning react!'},
  {id: 2, title: 'installation', content: 'you can install react from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);

React - 条件渲染

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


function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      Login
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      Logout
    </button>
  );
}

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) {
    console.log(isLoggedIn)
    return (
      <UserGreeting />
    );
  }
  return (
    <GuestGreeting />
  );
}

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

在 js 中,true && expression 返回 expression,而 false && expression 返回 false。

阻止组件渲染
可以让 render 返回 null,不进行任何渲染。

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(state => ({
      showWarning: !state.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')
);

React - 事件处理

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
1. React事件的命名采用驼峰,而不是纯小写
2. 使用 jsx 语法时需要传入一个函数作为事件处理函数,而不是一个字符串
3. 不能通过返回 false 的方式阻止没人行为。必须显式地使用 preventDefault。
4. 在使用 React 时,一般不需要使用 addEventListener 为已经创建的 DOM 元素添加监听器。只需要在该元素初始渲染的时候添加监听器即可。

事件处理程序传递参数

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

上述两种方法是等价的,分别通过箭头函数和 Function.prototype.bind 来实现。
在这两种情况下,React 的事件对象 e 会被当作第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的方式进行传递。

React - State 和生命周期

正确使用 State
1. 不要直接修改 state,而是应该使用 setState,构造函数是唯一可以给 this.state 赋值的地方。
2. State 的更新可能是异步的。出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。因为 this.props 和 this.state 可能会异步更新,所以不能依赖他们的值来更新下一个状态
3. State 的更新会被合并。当调用 setState() 的时候,React 会把提供的对象合并到当前的 state

数据是向下流动的
不管是父组件或者是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也不关心它是函数组件还是 class 组件。
这就是为什么称 state 为局部的或者是封装的原因。除了拥有并设置了它的组件,其他组件都无法访问。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中

React - 组件和 props

定义组件可以写函数或者创建 class

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

该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。

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

上述两个是等价的。

自定义组件必须开头大写。

所有 React 组件都必须像纯函数一样保护它们的 props 不被修改。

React - Tic Tac Toc

在 react 中,数据通过 props 传递,从父组件流向子组件。
可以通过 react 组建的构造函数中设置 this.state 来初始化 state,this.state 应该被视为一个组件的私有属性。
在 js class 中,每次定义子类的构造函数时,都需要调用 super 方法。因此在所有含有构造函数的 react 组件中,构造函数必须以 super(props) 开头。
在 render 中 onClick 事件监听函数中调用 this.setState, 就可以在每次 <button> 被点击的时候同志 react 去重新渲染 Square 组件。组件更新之后,Square 组件的 this.state.value 的值会变为 X。每次在组件中调用 setState 时,react 都会自动更新其子组件。
当遇到需要同时获取多个子组件数据,或者两个组件之间需要互相通讯的情况时,需要把子组件的 state 数据提升至其共同的父组件当中保存。之后父组件可以通过 props 将状态数据传递到子组件当中。这样应用当中所有组件的状态数据就能够方便地共享同步了。
在 React 中,有一个命名规范,通常会将代表事件的监听 prop 命名为on[Event], 讲处理事件的监听方法命名为 handle[Event]
不直接修改(或改变底层数据)这种方式和前一种的结果是一样的,好处有几点:

  1. 简化复杂的功能。不可变性使得复杂的特性更容易实现。可以做撤销喝恢复功能。不直接在数据上修改可以让我们追溯并复用游戏的历史记录。
  2. 跟踪数据的改变。如果直接修改数据,则很难跟踪到数据的改变。跟踪数据的改变需要可变对象可以与改变之前的版本进行对比,这样整个对象数都需要被遍历一次。跟踪不可变数据的变化相对来说容易多了。如果发现对象变成了一个新对象,那么我们就可以说对象发生改变了。


每当一个列表重新渲染时,React 会根据每一项列表元素的 key 来检索上一次渲染时与每个 key 所匹配的列表项。如果 react 发现当前的列表又一个之前不存在的 key,那么就会创建出一个新的组件。如果 React 发现和之前对比少了一个 key,那么就会销毁之前所对应的组件。如果一个组件的 key 发生了变化,这个组件会被销毁,然后使用新的 state 重新创建一份。
key 是 react 中一个特殊的保留属性(还有一个是 ref,拥有更高级的特性)。当 React 元素被创建出来的时候,react 会提取出 key 属性,然后把 key 直接存储在返回的元素上。虽然 key 看起来好像是 props 中的一个,但是你不能通过 this.props.key 来获取 可以。 React会用过 key 来自动判断哪些组件需要更新。组件是不能访问到它的 key 的。
每次只要构建动态列表的时候,都要指定一个合适的 key。如果没有找到一个合适的 key,那么就需要重新考虑整理你的数据结构了,这样才能有合适的 key。
如果你没有指定任何 key,react 会发出警告,并且会把数组的索引当作默认的 key。但是如果想要对列表进行重新排序、新增、删除操作时,把数组索引作为 key 是有问题的。显式地使用 key={i} 来指定 key 确实会消除警告,但是仍然和数组索引存在同样的问题,所以大多数情况虾最好不要这么做。
组件的 key 值不需要在全局都保证唯一,只需要在当前的同一级元素之前保证唯一即可。

Leetcode - Minimum Increment to Make Array Unique

https://leetcode.com/problems/minimum-increment-to-make-array-unique/

Given an array of integers A, a move consists of choosing any A[i], and incrementing it by 1.

Return the least number of moves to make every value in A unique.

 

Example 1:

Input: [1,2,2]
Output: 1
Explanation:  After 1 move, the array could be [1, 2, 3].

Example 2:

Input: [3,2,1,2,1,7]
Output: 6
Explanation:  After 6 moves, the array could be [3, 4, 1, 2, 5, 7].
It can be shown with 5 or less moves that it is impossible for the array to have all unique values.

 

Leetcode - Unique Email Addresses

https://leetcode.com/problems/unique-email-addresses/

Every email consists of a local name and a domain name, separated by the @ sign.

For example, in alice@leetcode.comalice is the local name, and leetcode.com is the domain name.

Besides lowercase letters, these emails may contain '.'s or '+'s.

If you add periods ('.') between some characters in the local name part of an email address, mail sent there will be forwarded to the same address without dots in the local name.  For example, "alice.z@leetcode.com" and "alicez@leetcode.com" forward to the same email address.  (Note that this rule does not apply for domain names.)

If you add a plus ('+') in the local name, everything after the first plus sign will be ignored. This allows certain emails to be filtered, for example m.y+name@email.com will be forwarded to my@email.com.  (Again, this rule does not apply for domain names.)

It is possible to use both of these rules at the same time.

Given a list of emails, we send one email to each address in the list.  How many different addresses actually receive mails? 

 

Example 1:

Input: ["test.email+alex@leetcode.com","test.e.mail+bob.cathy@leetcode.com","testemail+david@lee.tcode.com"]
Output: 2
Explanation: "testemail@leetcode.com" and "testemail@lee.tcode.com" actually receive mails