[干货]一篇笔记入门React

个人笔记, 转载请注明

转载自 szhshp的第三边境研究所

Refs and the DOM

In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.

一般情况下想要刷新child elem必须通过新的prop刷新, 但是其实可以有另一个方法, 就是使用ref

When to Use Refs

There are a few good use cases for refs:

  • Managing focus, text selection, or media playback.
  • Triggering imperative animations.
  • Integrating with third-party DOM libraries.

Avoid using refs for anything that can be done declaratively.

For example, instead of exposing open() and close()methods on a Dialog component, pass an isOpen prop to it.

使用ref之前切记要确定一下对应的属性是不是可以使用prop代替, 开发时应该尽可能遵从React推荐的信息流

Adding a Ref to a DOM Element

React supports a special attribute that you can attach to any component. The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.

关于ref有这些特点:

  1. 可以给任何DOM提供这个attr
  2. 提供给ref的value应该是个callback
  3. 这个callback可以在mounted/unmounted的时候调用
  • 注意会在mounted/unmounted的时候执行一次, 而不是每次渲染的时候执行
  1. 当提供这个参数给HTML elem的时候, 这个ref指向的callback会获取当前DOM elem作为参数.

例子:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // Explicitly focus the text input using the raw DOM API
    // 3. 因此这儿可以通过this.textInput调用focus()方法
    this.textInput.focus();
  }

  render() {
  
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    // 1. Component载入的时候就会执行ref里面的callback.
    // 2. 然后这个Elem会当做参数传入, 因此就可以通过this.textInput引用这个DOM elem
    
    
    return (
      
{ this.textInput = input; }} />
); } }

Adding a Ref to a Class Component

类似ref在HTML DOM中的使用, 将ref赋值给自定义Component的时候, ref对应的callback就会接受自身这个instance作为参数

注意: 必须要这个自定义Component是个完整的class, 不可以是函数

When the ref attribute is used on a custom component declared as a class, the ref callback receives the mounted instance of the component as its argument. For example, if we wanted to wrap the CustomTextInput above to simulate it being clicked immediately after mounting:

class AutoFocusTextInput extends React.Component {
  componentDidMount() {
    // 2. 这儿就可以对这个instance进行引用
    this.textInput.focusTextInput();
  }

  render() {
    return (
       { this.textInput = input; }} />
    );
  }
}

Exposing DOM Refs to Parent Components

一般数据流不应该在Parent Class里面控制Child Class, 但是偶尔就有这样傻逼的需求.

这时候用ref最适合了, 但是依然不推荐这么使用.

更好的解决办法是你和需求分析的人打一架然后让他根据代码改需求

当然上文提到了解决办法是让child class暴露一个prop, 但是这个办法无法对function Component使用.

另外下面这个办法对function Component也有用:

function CustomTextInput(props) {
  // 2. 在child component里面将这个callback赋值给ref
  // 2+. 当然这儿只能用ref这个attr
  // 3. 然后这个child component在mount/unmount的时候就会执行这个callback
  return (
    
); } class Parent extends React.Component { render() { // `Parent` passes its ref callback as an `inputRef`prop to the `CustomTextInput` // 1. 这儿提供一个callback作为props.inputReg return ( this.inputElement = el} /> ); } }

最终在parent component中就可以通过inputElement这个变量调用到对应输入框元素.

而且这个可以逐级传递, 比如下面这个隔代传递的例子:

function CustomTextInput(props) {
  return (
    
); } function Parent(props) { return (
My input:
); } class Grandparent extends React.Component { render() { return ( this.inputElement = el} /> ); } }

看起来暴露DOM有些别扭, 但是很多时候需求就是比较特别. 因此上面是推荐用法.

旧版本React里面曾经有过使用this.refs.textInput的用法, 其中ref会指向一段字符串, 新版本React不推荐如此使用

注意事项

如果ref是个inline function, updates的时候会被调用两次. 第一次是null然后第二次是DOM Elem.

这个是因为update的时候React需要清理旧的并且建立新的component

Solutions

为了防止这个问题, 可以将ref callback和对应class进行绑定.

即使不绑定也不会引发太多问题

JSX In Depth

JSX将通过Babel进行编译.

标准首位标签格式


  Click Me


compiles into:

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

自闭合标签格式

You can also use the self-closing form of the tag if there are no children. So:

compiles into:

React.createElement(
  'div',
  {className: 'sidebar'},
  null
)

Specifying The React Element Type

一般Component使用大写字母开头, 如果在当前JSX里面使用那么XXX这个Component就必须出现在Scope之内.

另外可以使用import语句将Component引用到Scope内

Using Dot Notation for JSX Type

import React from 'react';

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return 
Imagine a {props.color} datepicker here.
; } } //下方使用圆点标识符调用了子属性 function BlueDatePicker() { return ; }

User-Defined Components Must Be Capitalized

自定义Component(比如 )应该大写字母开头
系统自带的Component(比如

)应该小写字母开头

否则系统会抛出警告

Choosing the Type at Runtime

注意JSX Obj的tags必须是完整的单词, 如果想要动态生成tag名称可以用赋值的办法

错误的例子:

function Story(props) {
  // Wrong! JSX type can't be an expression.
  return ;
}

正确的做法:

function Story(props) {
  // Correct! JSX type can be a capitalized variable.
  const SpecificStory = components[props.storyType];
  return ;
}

Prop in JSX

JavaScript Expressions as Props

关于使用JS表达式作为prop:



If condition within JSX

不能够直接使用, 但是可以用这个替代方法:

function NumberDescriber(props) {
  let description;
  
  // check the condition
  if (props.number % 2 == 0) {
    description = even;
  } else {
    description = odd;
  }
  
  
  return 
{props.number} is an {description} number
; }

另外一个很实用的条件判断的snippet

This can be useful to conditionally render React elements. This JSX only renders a

if showHeader is true:


{showHeader &&
}


### String Literals


When you pass a string literal, its value is HTML-unescaped. So these two JSX expressions are equivalent:





### Props Default to “True”


If you pass no value for a prop, it defaults to true. These two JSX expressions are equivalent:



### Spread Attributes


If you already have `props` as an object, and you want to pass it in JSX, you can use `...` as a “spread” operator to pass the whole props object. These two components are equivalent:

>提供Props时, 若有一个Obj并且这个Obj里面所有Attr和需要的Prop相匹配, 那么可以使用`spread` operator 进行赋值


function App1() {
return ;
}

function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return ;
}




另外对于`spread` operator也可以给部分值赋值:



const Button = props => {

//将传进函数里面的props.kind 赋值给kind, 将其他属性赋值给...other
const { kind, ...other } = props;

const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
//然后将...other传给button这个elem, 而没有传递kind这个参数
return


);
};


> # 关于解构运算符
>
>MDN参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
>
这玩意是ES6新引进的运算符, 通过三个点进行引用`...`
>
> ```
>function sum(x,y,z){
  return x+y+z
}
>
console.log(sum(...[1,2,3])) //6
console.log(sum(...[1,2,3,4,5])) //6
console.log(sum(...[1,,3,4,5])) //NaN
>
var doSomething = function(...args){
  return sum(..args)
}
>
console.log(doSomething(...[1,2,3])) //6
>
//对Object是否有效呢?
var obj = { a:"A", b:"B", c:"C", d:"D", e:"E" }
var {a,b,...others} = obj //报错var {a,b,...others} = obj //报错

在老版本ES6中, 对Obj进行解构赋值会报错, 但是JSX中允许这样的使用.

Children in JSX

子元素全部会通过props.children进行引用.

另外如果没有子tag而只是放置了一段文本, 那么props.children就会是tag之间的一段字符串

Hint: HTML is unescaped!

Hello world!

This is valid HTML & JSX at the same time.

tags之间的空格和空行都会被忽略.

Hello World

另外也可以将JS表达式用于child elem. 比如一个很典型的使用数组循环生成列表的例子:

  return (
    
    {todos.map((message) => )}
);

Functions as Children

反正只要最后返回的是一个完整的JSX Obj即可


// 2. 我们在渲染parent elem
function Repeat(props) {
  let items = [];
  for (let i = 0; i < props.numTimes; i++) {
    // 3. 通过调用child elem对应的函数生成jsx obj
    items.push(props.children(i));
  }
  return 
{items}
; } // 1. 返回的JSX obj的child elem是个函数 function ListOfTenThings() { return ( {(index) =>
This is item {index} in the list
}
); }

Booleans, Null, and Undefined Are Ignored

false, null, undefined, and true are valid children. They simply don’t render. These JSX expressions will all render to the same thing:

{false}
{null}
{undefined}
{true}

Thinking in React

就是React项目流程的rethinking

Step 1: Break The UI Into A Component Hierarchy

  1. draw boxes around every component (and subcomponent) in the mock and give them all names
  • 使用一些规则(比如单一原则)判断元素应该属于哪些部分

[图片上传失败...(image-af5c7e-1512723756201)]

  1. 得到Component层次列表
  2. 决定数据模型

Step 2: Build A Static Version in React

  1. 先写个静态的
  • 重用的Component要准备好
  • 静态的部分应该不需要用到任何State, State应该仅仅用于动态的Component
  • 从上往下或者从下往上建立都可以, 小型项目从上往下会快一些, 大型项目反之
  1. 改为动态模型
  • 一般最大的那个Component会接受数据作为全部数据源

Step 3: Identify The Minimal (but complete) Representation Of UI State

  1. 建立项目的时候, 最好能先确定minimal set of mutable state, 也就是最小动态模块
  2. 判断一下动态模块是否可以设置成state

之前提到过的三个原则进行判断:

  1. Is it passed in from a parent via props? If so, it probably isn’t state.
  2. Does it remain unchanged over time? If so, it probably isn’t state.
  3. Can you compute it based on any other state or props in your component? If so, it isn’t state.

Step 4: Identify Where Your State Should Live

  1. 决定哪些Component应该拥有这些State, 哪些Component应该让State进行变化

React从头到尾就只有一条数据流有这些需要注意的:

  1. 可以通过自身State进行render的Component
  2. 最大的那个Component应该拥有State, 用于流程管理之类的业务, 这个State可能包含所有动态成分的内容
  3. 如果无法找到一个值得拥有一个State的Component, 那就写个新的, 可能可以放在最大的State的更上一层

Step 5: Add Inverse Data Flow

就是数据的各种交互呗

一个重要的概念:

有些时候parent class可能会给child class提供一个eventHandler()

这玩意可以当做一种callback, 赋值给child class的onchange, child class通过调用这个onChange使得parent class的state变化.

如果只有一个值的情况下甚至可以不用给child class设置任何的state# React: 关于setState()

React&Error: setState()不立刻响应到DOM, 总是慢一步

这个八成是因为在setState的同一个方法里面调用了this.state

这时候获取到的state都是前一个state

下方是参考文献:


原文: https://medium.com/@mweststra...
作者: Michel Weststrate

前言

这篇文章原标题是3 Reasons why I stopped using React.setState,但是我对原文作者提出的论点不是很感冒,但是作者提出的三点对React新手来说是很容易忽略的地方,所以我在这里只提出部分内容,而且把标题改为使用React.setState需要注意的三点

正文

React新手来说,使用setState是一件很复杂的事情。即使是熟练的React开发,也很有可能因为React的一些机制而产生一些bug,比如下面这个例子:

文档中也说明了当使用setState的时候,需要注意什么问题:

注意:
绝对不要 直接改变this.state,因为之后调用setState()可能会替换掉你做的改变。把this.state当做是不可变的。

setState()不会立刻改变this.state,而是创建一个即将处理的state转变。在调用该方法之后访问this.state可能会返回现有的值。

setState的调用没有任何同步性的保证,并且调用可能会为了性能收益批量执行。

setState()将总是触发一次重绘,除非在shouldComponentUpdate()中实现了条件渲染逻辑。如果可变对象被使用了,但又不能在shouldComponentUpdate()中实现这种逻辑,仅在新state和之前的state存在差异的时候调用setState()可以避免不必要的重新渲染。

总结出来,当使用setState的时候,有三个问题需要注意:

1. setState是异步的(译者注:不保证同步的)

很多开发刚开始没有注意到setState是异步的。如果你修改一些state,然后直接查看它,你会看到之前的state。这是setState中最容易出错的地方。 setState这个词看起来并不像是异步的,所以如果你不假思索的用它,可能会造成bugs。下面这个例子很好的展示了这个问题:

class Select extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.state = {
      selection: props.values[0]
    };
  }

  render() {
    return (
      
    {this.props.values.map(value =>
  • this.onSelect(value)} > {value}
  • )}
) } onSelect(value) { this.setState({ selection: value }) this.fireOnSelect() } onKeyDown = (e) => { const {values} = this.props const idx = values.indexOf(this.state.selection) if (e.keyCode === 38 && idx > 0) { /* up */ this.setState({ selection: values[idx - 1] }) } else if (e.keyCode === 40 && idx < values.length -1) { /* down */ this.setState({ selection: values[idx + 1] }) } this.fireOnSelect() } fireOnSelect() { if (typeof this.props.onSelect === "function") this.props.onSelect(this.state.selection) /* not what you expected..*/ } } ReactDOM.render( //绑定onChange ); }

The textarea Tag

有一点和H5不同的地方, React端的value的设置可以通过一个attr:

H5:


React: