react初级学习

学习整理React官方文档整理如下
https://react.docschina.org/docs/getting-started.html

相比jQuery不需要DOM操作,虚拟DOM
声明式

跨平台
DOM Diff算法,最小化页面重绘

JSX

JSX 防止注入攻击,可以安全地在 JSX 当中插入用户输入内容

React DOM 在渲染所有输入内容之前,默认会进行转义成了字符串。可以确保在应用中不会注入那些并非自己明确编写的内容。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。

vue {{转义成字符串}}

const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;

本质是React.createElement

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

在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象。

可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX,然后通过{JSX}可以将JSX注入DOM中。

通过花括号包裹代码,可以在 JSX 中嵌入任何表达式。
利用JavaScript 中的逻辑与 (&&) 运算符,三目运算符,可以很方便地进行元素的条件渲染。

true && expression 返回 expression
false && expression 总是会返回 false

隐藏组件:在组件的 render 方法中返回 null 并不会影响父组件的生命周期。
CSS样式,适用于频繁切换display: !isEditing ? undefined : ‘none’,

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,不进行任何渲染。 并且不会影响组件的生命周期,钩子函数依然会被调用。

父组件与子组件

父组件中,通过标签传入的属性挂载在子组件的props对象下。
在父组件中使用children prop 来将他们的子组件传递到渲染结果

children prop

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.createPortal

弹窗、全局组件等开发的情况,这时候我们可能根据不同的需求需要把组件渲染至指定页面
ReactDOM.createPortal使用实例
ReactDOM.createPortal(child, container)

组件

元素
React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()。(DOM渲染更新时候自己会做优化,此外也可以考虑有状态组件)
style对象是常量,style.margin可变

组件名称必须以大写字母开头
组件决不能修改自身的 props,只允许随用户操作、网络响应或者其他变化而动态更改输出内容state
props用于定义外部接口,state用于记录内部状态
如果顶层(父级)的props是只读的,某个props改变了,React会重渲染所有的子节点。props主要用于整个组件树中传递数据和配置。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中,父组件可以通过children prop 来将他们的子组件传递到渲染结果中
React 中的数据流是单向的,顺着组件层级从上往下传递。

对于组件的state选取,可以尝试通过以下步骤来判断:
对于应用中的每一个 state:
找到根据这个 state 进行渲染的所有组件。
找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
该共同所有者组件或者比它层级更高的组件应该拥有该 state。
如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。

在React的组件中可以在两个位置来初始化state:
(1)在组件的constructor中,等同于在类内部;(2)直接在class中利用属性赋值的方式this.setState
大多数情况下都不要使用prop来为State的初始化赋值,因为这会让你的数据来源不唯一,这常常会导致Bug。数据源唯一是最佳实践。

补充_对比react、vue

react是单向数据绑定,vue中的特色是双向数据绑定。
其实两者都提倡单向数据流去管理状态。vuex和redux状态管理器就是单向,只是vue为UI控件提供了双向数据绑定(v-model本质就是value 的单向绑定 + onChange 事件侦听)的方式,适应一些需要实时反应用户输入的场合,但通常在复杂应用中这种便利比不上引入状态管理带来的优势。

在 JavaScript 中对象和数组是通过引用传入的,这时为了防止子组件影响到父组件,赋值时注意引用props的副本

state:驱动应用的数据源。view:以声明方式将 state 映射到视图 。 actions:响应在 view 上的用户输入导致的状态变化

vuex和redux解决:
多个组件共享状态破坏单向数据流的简洁性,即多个视图依赖于或者其行为变更同一状态
把组件的共享状态抽取出来,以一个全局单例模式管理。另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,代码将会变得更结构化且易维护。

在大型项目中React 优势不言而喻
Vue 的数据对象相比 React 的状态对象
Vue 的单文件组件,使用 < template> 、< script> 分割代码,直接导致的问题就是上下文丢失,方法需要通过methods传递
Vue 推荐的方案只有强耦合的 Vuex(Redux 迁移到 Vue 等不算在内)
React 周边方案有 Redux、Mobx 等。这些库不会与 React 有太强的耦合(可以独立存在)。
两个框架的状态管理思想差不多,都是单向数据流、单例模式(Vuex & Redux)。

定义组件

定义组件的方式:函数组件、 class组件

最简单,编写 JavaScript 函数,返回JSX
函数式组件就只有props没有state, react也非常鼓励编写函数式组件。

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

还可以使用 ES6 的 class 来定义组件:

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

定时器例子

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);
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')
);

关于 setState()

将传参对象合并到组件当前的状态值,构建一个新的虚拟DOM,对比旧的,更新

this.props 和 this.state 可能会异步更新,出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用,所以不要依赖他们的值来更新下一个状态。

不要直接修改 State(不会重新渲染组件),而是应该使用 setState(),React 会把你提供的对象合并到当前的 state。构造函数是唯一可以给 this.state 赋值的地方
传参对象 ,传参函数可以解决这个问题。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:

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

例子2

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

提取组件

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          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-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

提取为

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

React.Component 的子类中必须定义 render() 函数

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

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

状态提升_组件共享数据

通过state提升,依靠自上而下的数据流,而不是尝试在不同组件间同步 state。

在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可通过props实现共享 父组件的state,多个组件通过调用父组件上 包装了setState的方法 对state进行修改,反映相同的变化数据。

例子_根据传入props渲染不同的结果

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

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

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

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />
        <TemperatureInput scale="f" />
      </div>
    );
  }
}

例子_在父组件中同步子组件的数据

子组件对于父组件传入的 prop没有控制权
在 React 中,通过使用“受控组件”来解决这个问题。类似与 DOM 中的 接受 value 和 onChange 一样,子组件接收两个来自父组件 的props,一个值一个方法的调用

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

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

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

生命周期

组件创建的钩子函数

当组件实例被创建并插入 DOM 中的生命周期调用顺序如下:

  1. constructor() 初始化状态、事件处理程序
  2. static getDerivedStateFromProps()
  3. render()
  4. componentDidMount()
constructor(props) {
  super(props);
  // 不要在这里调用 this.setState()
  //避免将 props 的值复制给 state!这是一个常见的错误
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
//会在组件挂载后(插入 DOM 树中)立即调用
//依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,适合实例化请求
//适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅
}

组件更新的钩子函数

组件的 props 或 state 发生变化时,会触发更新的生命周期调用顺序如下:

  1. static getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()
componentDidUpdate(prevProps, prevState, snapshot){
//会在更新后会被立即调用。首次渲染不会执行此方法。
// 典型用法(不要忘记比较 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

组件卸载的钩子函数

当组件从 DOM 中移除时会调用:

componentWillUnmount(){
//会在组件卸载及销毁之前直接调用
//在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
}

componentWillUnmount() 中不应调用 setState()
因为该组件将永远不会重新渲染

组件抛出错误的钩子函数

当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

static getDerivedStateFromError()

componentDidCatch(error, info){
//error —— 抛出的错误。
//info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。
//用于记录错误之类的情况:
}
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染可以显示降级 UI
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // "组件堆栈" 例子:
    //   in ComponentThatThrows (created by App)
    //   in ErrorBoundary (created by App)
    //   in div (created by App)
    //   in App
    logComponentStackToMyService(info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // 你可以渲染任何自定义的降级 UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

setState() 和 forceUpdate()

setState() 是异步操作,会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。
所以请使用 componentDidUpdate 或者 setState 的回调函数(setState(updater, callback)),保证在应用更新后触发。
如需基于之前的 state 来设置当前的 state,关注参数 updater 的内容。

默认当组件的 state 或 props 发生变化时,组件将重新渲染。
但是如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件调用 render() 重新渲染。
应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state。

一个例子

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 根据 W3C 规范来将浏览器原生事件封装为合成事件,并传入设置的事件处理程序中。合成事件提供了与原生事件同样的接口。
此外,React实际上没有将事件附加到子节点本身,而是通过事件委托模式,使用单个事件监听顶层所有事件来优化性能。

https://reactjs.bootcss.com/docs/events.html

function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

React 一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器,而是在该元素初始渲染的时候添加监听器即可。
使用 ES6 class 语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法。

例如: Toggle 组件会渲染一个让用户切换开关状态的按钮:

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

必须谨慎对待 JSX 回调函数中的 this,这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理有关。
如果没有在方法后面添加 (),例如 onClick={this.handleClick},那么应该手动为这个方法绑定 this
在 JavaScript 中,class 的方法默认不会绑定(类的实例 this)。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。

bind绑定之外,这里有两种方式可以解决。public和箭头函数

我的理解:给onclick赋值箭头函数

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

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

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

向处理程序传递参数
箭头函数(显式)和 Function.prototype.bind (隐式)

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

列表

可以通过使用 {} 在 JSX 内构建一个DOM元素集合

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 numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

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

提供key,数组元素中使用的 key 在其兄弟节点之间应该是独一无二的,不需要是全局唯一。

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
  //提供key给列表
   //
  • {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 只有放在就近的数组上下文中才有意义,(在数组的层次上给每个item赋值)先简单理解为:在 map() 方法中的元素需要设置 key 属性
    e.g:你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 < ListItem /> 元素上,而不是放在 ListItem 组件中的 < li> 元素上。

    function ListItem(props) {
      const value = props.value;
      return (
        // 错误!你不需要在这里指定 key:
        <li key={value.toString()}>
          {value}
        </li>
      );
      //改为 return 
  • {props.value}
  • ;
    } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 错误!元素的 key 应该在这里指定: <ListItem value={number} /> // key 应该在数组的上下文中被指定 //改为 ); return ( <ul> {listItems} </ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') );

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

    表单

    表单元素通常会保持一些内部的 state。
    表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面。

    class EssayForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
        };
    
        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>
              文章:
              <textarea value={this.state.value} onChange={this.handleChange} />
            </label>
            <input type="submit" value="提交" />
          </form>
        );
      }
    }
    

    select 受控组件

    受控组件:state随着用户交互改变

    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('你喜欢的风味是: ' + this.state.value);
        event.preventDefault();
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              选择你喜欢的风味:
              <select value={this.state.value} onChange={this.handleChange}>
                <option value="grapefruit">葡萄柚</option>
                <option value="lime">酸橙</option>
                <option value="coconut">椰子</option>
                <option value="mango">芒果</option>
              </select>
            </label>
            <input type="submit" value="提交" />
          </form>
        );
      }
    }
    

    开启多选之后,传入value一个数组实现多选

    <select multiple={true} value={['B', 'C']}>
    

    input 受控组件

    当需要处理多个 input 元素时,可以给每个元素添加 name 属性,在同一个处理函数中根据 event.target.name 的值选择要执行的操作。
    在受控组件上通过 prop 指定 value 的值会阻止用户更改输入。但value 设置为 undefined 或 null,输入仍可编辑。

    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>
              参与:
              <input
                name="isGoing"
                type="checkbox"
                checked={this.state.isGoing}
                onChange={this.handleInputChange} />
            </label>
            <br />
            <label>
              来宾人数:
              <input
                name="numberOfGuests"
                type="number"
                value={this.state.numberOfGuests}
                onChange={this.handleInputChange} />
            </label>
          </form>
        );
      }
    }
    

    input 非受控组件

    使用 ref 来从 DOM 节点中获取表单数据:

    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.input = React.createRef();
      }
    
      handleSubmit(event) {
        alert('A name was submitted: ' + this.input.current.value);
        event.preventDefault();
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              Name:
             <input
              defaultValue="Bob"
              type="text"
              ref={this.input} />
            </label>
            <input type="submit" value="Submit" />
          </form>
        );
      }
    }
    

    input file 非受控

    < input type=“file” /> 始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。

    <input type="file" />
    

    允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API 进行控制。

    const selectedFile = document.getElementById('input').files[0];
    
    class FileInput extends React.Component {
      constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.fileInput = React.createRef();
      }
      handleSubmit(event) {
        event.preventDefault();
        alert(
          `Selected file - ${this.fileInput.current.files[0].name}`
        );
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              Upload file:
              <input type="file" ref={this.fileInput} />
            </label>
            <br />
            <button type="submit">Submit</button>
          </form>
        );
      }
    }
    
    ReactDOM.render(
      <FileInput />,
      document.getElementById('root')
    );
    

    Context跨层级的组件通信

    Context 设计目的是为了共享那些对于一个组件树而言是**** “全局”的数据****,例如当前认证的用户、主题或首选语言。即多个层级的多个组件需要访问相同数据

    //创建一个 Context 对象
    const MyContext = React.createContext(defaultValue);
    //当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值,没有匹配到则采用defaultValue
    <MyContext.Provider value={/* 某个值 */}>
    //Consumer中取的就是Provider的value
    
    

    每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。Provider组件利用Object.is检测 value 的值是否有更新,变化则其所有后代Consumers都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

    Provider 包裹的组价内部可以通过Consumers访问到 Provider 的value值

    Class.contextType

    挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。此属性能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。

    class MyClass extends React.Component {
      componentDidMount() {
        let value = this.context;
        /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
      }
      componentDidUpdate() {
        let value = this.context;
        /* ... */
      }
      componentWillUnmount() {
        let value = this.context;
        /* ... */
      }
      render() {
        let value = this.context;
        /* 基于 MyContext 组件的值进行渲染 */
      }
    }
    MyClass.contextType = MyContext;
    

    错误边界

    Refs转发

    在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它。但是,在某些情况下,你需要在典型数据流之外强制修改子组件。
    当属性值是一个回调函数的时候,函数将接受底层的DOM元素或组件的已挂载实例作为其第一个参数,可以在组件中存储他

    fragment

    React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。

    高阶组件HOC

    组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。
    e.g: Redux 的 connect 和 Relay 的 createFragmentContainer。
    HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。

    React三种更新的方式:
    (1)ReactDOM.render() || hydrate(ReactDOMServer渲染)
    ReactDOM.render(element, container,callback?){在c内渲染一个e,并返回对该组件的引用,该回调将在组件被渲染或更新之后被执行}
    (2)setState
    (3)forceUpdate

    函数式组件

    React数据流中,props是唯一的父组件与它们的子元素的通信方式。
    更改子元素,你需要使用新的props去重新渲染子元素。
    React提供了一种特殊方法:Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧(慎用)
    当ref属性用于HTML元素,在构造器中通过React.createRef()函数创建的ref接收底层DOM元素作为它的current属性;
    当ref属性用于传统的类组件,ref对象接收挂载好的组件实例作为它的current;
    你不能将ref属性用于函数式组件上,因为他们并没有实例(instance)!

    React.Children

    React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。不能保证props.children是数组,通过这种方式来实现遍历props.children

    React.Children.map(props.children,()=>{})
    

    https://zh-hans.reactjs.org/docs/react-api.html#reactchildrenmap

    规范

    https://guide.aotu.io/docs/js/react.html
    React 组件使用 PascalCase,组件实例和React DOM属性名称 使用 camelCase,属性值用单引号’value’,当属性值为true时可以只写key

    Refs避免使用字符串引用,请使用回调函数作为引用

    <Foo
      ref={ref => { this.myRef = ref }}
    />
    

    建议尽量使用函数式组件配合 Hooks 来进行开发

    class extends React.Component的顺序:
    可选的 static 方法
    constructor
    getChildContext
    componentWillMount
    componentDidMount
    componentWillReceiveProps
    shouldComponentUpdate
    componentWillUpdate
    componentDidUpdate
    componentWillUnmount
    事件处理函数 例如onClickSubmit()或onChangeDescription()
    render的getter方法 如getSelectReason() or getFooterContent()
    可选的render方法 如 renderNavigation() or renderProfilePicture()
    render

    React.createClass的顺序: eslint: react/sort-comp
    displayName
    propTypes
    contextTypes
    childContextTypes
    mixins
    statics
    defaultProps
    getDefaultProps
    getInitialState
    getChildContext
    componentWillMount
    componentDidMount
    componentWillReceiveProps
    shouldComponentUpdate
    componentWillUpdate
    componentDidUpdate
    componentWillUnmount
    事件处理函数 如 onClickSubmit() 或 onChangeDescription()
    render的getter方法 如 getSelectReason() 或 getFooterContent()
    可选的render方法 如 renderNavigation() 或 renderProfilePicture()
    render

    你可能感兴趣的:(react,react)