[TOC]

react常用语法

1. react样式的写法

var HelloWorld = React.createClass({  
  render:function(){  
    var styles = {   color: 'blue',  fontSize: '30'  }  
    return (  
      <div className="box">  
        <h3 className="title" 
          style={{color:'red',backgroundColor:'lime'}}>默认标题</h3>  
        <p className="subtitle" style={styles}>说明</p>  
        <p className="details">这个是用来教学的案例</p>  
      </div>  
    )  
  }  
})  
ReactDOM.render(<HelloWorld/>,document.getElementById("app"))  
  • JSX中使用样式

    1、行内样式:写行内样式的时候需要使用两个{}  ==>{{}}

    2、对象样式:在return前面定义一个样式对象,注意样式的写法,与HTML的不同点

    3、CSS样式

  • 在HTML5中与在React中的样式的书写区别

    1、HTML5中以;结束,在React中以,结束

    2、在HTML5中属性与值都不需要加上引号。在React中,属于javascript对象,key中不能存在 -,需要使用驼峰命名,如果是value值,需要加上引号

    3、在HTML中,设置带数字的值,宽度,高度==,需要带上单位。在React中可以不用带单位,直接写数字 这里是指那些规定了默认单位的值。比如说像素px,如果要使用em或者是rem则需要加上单位

  • 其他注意事项

    在使用插值符号的时候,里面需要时一个对象或者是一个表达式

参考资料:https://blog.csdn.net/chuipaopao163/article/details/73432229

1.1 JSX中根据条件显示特殊样式

<div className={(this.state.menuIndex === i ? 'active' : '')} />

1.2 多个类样式根据不同条件来显示

可使用npm包工具来实现:https://www.npmjs.com/package/classnames

或者自己根据条件来拼字符串:

setClassNames = (obj) => {
  if ( typeof obj !== 'object') {
    return '';
  }
  let key;
  let str = '';
  for (key in obj) {
    if(obj[key]) {
      str += ' ' + key;
    }
  }
  return str;
}
return (
<span
   className={this.setClassNames({
       'active': this.state[e.key] === item.value,
       'forbidden': this.state.dimensional === e.value,
   })}
>{item.name}</span>
)

2. 生命周期(Lifecycle)

https://segmentfault.com/a/1190000004168886

https://segmentfault.com/a/1190000018490987

React 的生命周期包括三个阶段:mount(挂载)、update(更新)和 unmount(移除)

2.1 mount

mount 就是第一次让组件出现在页面中的过程。这个过程的关键就是 render 方法。React 会将 render 的返回值(一般是虚拟 DOM,也可以是 DOM 或者 null)插入到页面中。

这个过程会暴露几个钩子(hook)方便你往里面加代码:

  1. constructor()
  2. componentWillMount()
  3. render()
  4. componentDidMount()

我用一幅图解释一下:

2.2 update

mount 之后,如果数据有任何变动,就会来到 update 过程,这个过程有 5 个钩子:

  1. componentWillReceiveProps(nextProps) - 我要读取 props 啦!

  2. shouldComponentUpdate(nextProps, nextState) - 请问要不要更新组件?true / false

  3. componentWillUpdate() - 我要更新组件啦!

  4. render() - 更新!

  5. componentDidUpdate() - 更新完毕啦!

2.3 unmount

当一个组件将要从页面中移除时,会进入 unmount 过程,这个过程就一个钩子:

  1. componentWillUnmount() - 我要死啦!

你可以在这个组件死之前做一些清理工作。

3. ref的使用

ref是使用回调函数的方式去使用:

class Input extends Component {
  focus = () => { this.textInput.focus(); }
  render(){
    return (<input ref={(input) => { this.textInput = input }} />)
	}
}

input参数来源:

当我们在DOM Element中使用ref时,回调函数将接收当前的DOM元素作为参数,然后存储一个指向这个DOM元素的引用。那么在示例代码中,我们已经把input元素存储在了this.textInput中,在focus函数中直接使用原生DOM API实现focus聚焦。

父组件使用ref调用子组件方法:

class Son extends React.Component {
  getShowData = (params) => { console.log('params ', params) }
  render() { return ( <div>12312</div> ) }
}
class father extends React.Component {
  // 获取子组件引用
  getRef = (ele) => {
    this.incomeTable = ele;
    if (ele) {
      const params = {a: 1, b: 2}
      ele.getShowData(params);
    }
  }
  render() {
    return (  <Son  ref={this.getRef} /> )
  }
}
  • ref的回调函数执行时间

    当组件挂载后和卸载后,以及ref属性本身发生变化时,回调函数就会被调用。

可以在组件实例中使用ref

// <Input>来源于上面的示例代码👆
class AutoFocusTextInput extends Component {
  componentDidMount(){
    this.textInput.focus();
  }
  render(){
    return (
      <Input ref={(input) => { this.textInput = input }}>
    )
	}
}

当我们在<Input>中添加ref属性时,其回调函数接收已经挂载的组件实例<Input>作为参数,并通过this.textInput访问到其内部的focus方法。也就是说,上面的示例代码实现了当AutoFocusTextInput组件挂载后<Input>组件的自动聚焦。

接下来文档指出,<Input>组件必须是使用class声明的组件,不然无法使用。这意味着React逐渐与ES6全面接轨了。

父组件的ref回调函数可以使用子组件的DOM:

这是Facebook非常不推荐的做法,因为这样会打破组件的封装性,这种方法只是某些特殊场景下的权宜之计。我们看看如何实现,上代码:

function CustomTextInput(props) {
  return (<input ref={props.inputRef} />);
}
class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput inputRef={el => this.inputElement = el} />
		);
	}
}

原理就是父组件把ref的回调函数当做inputRefprops传递给子组件,然后子组件<CustomTextInput>把这个函数和当前的DOM绑定,最终的结果是父组件<Parent>this.inputElement存储的DOM是子组件<CustomTextInput>中的input

同样的道理,如果A组件是B组件的父组件,B组件是C组件的父组件,那么可用上面的方法,让A组件拿到C组件的DOM。但是官方态度是discouraged,这种多级调用确实不雅,我们确实需要考虑其他更好的方案了。

参考资料:https://juejin.im/post/5927f51244d904006414925a

4.Context:组件间共享变量

Context是react 16.0以上版本才支持的。

注意:使用Context共享变量时,要是一个React.createContext创建的才能共享

4.1 API说明

  • React.createContext

    const {Provider, Consumer} = React.createContext(defaultValue);
    

    创建一对 { Provider, Consumer }。当 React 渲染 context 组件 Consumer 时,它将从组件树的上层中最接近的匹配的 Provider 读取当前的 context 值。

    如果上层的组件树没有一个匹配的 Provider,而此时你需要渲染一个 Consumer 组件,那么你可以用到 defaultValue 。这有助于在不封装它们的情况下对组件进行测试。

  • Provider

    <Provider value={/* some value */}>
    

    React 组件允许 Consumers 订阅 context 的改变。

    接收一个 value 属性传递给 Provider 的后代 Consumers。一个 Provider 可以联系到多个 Consumers。Providers 可以被嵌套以覆盖组件树内更深层次的值。

  • Consumer

    <Consumer>
      {value => /* render something based on the context value */}
    </Consumer>
    

    一个可以订阅 context 变化的 React 组件。

    接收一个 函数作为子节点. 函数接收当前 context 的值并返回一个 React 节点。传递给函数的 value 将等于组件树中上层 context 的最近的 Provider 的 value 属性。如果 context 没有 Provider ,那么 value 参数将等于被传递给 createContext()defaultValue

每当Provider的值发送改变时, 作为Provider后代的所有Consumers都会重新渲染。 从Provider到其后代的Consumers传播不受shouldComponentUpdate方法的约束,因此即使祖先组件退出更新时,后代Consumer也会被更新。

通过使用与Object.is相同的算法比较新值和旧值来确定变化。

4.2 一个使用的例子:

// theme-context.js  声明一个Context
import React from 'react';
export const {Provider, Consumer} = React.createContext();

// app.jsx 把变量发布出去
import { Provider } from './userContext'   
class App extends React.Component {
  render () {
    return (
      <Provider value={this.state.userInfo}>
        <div>巴拉巴拉。。。</div>
      </Provider>
    )
  }
}
// 获取变量有2中方式:
// 1. 把获取的变量当组件的属性传给 组件
// BusinessIncome.jsx 
class BusinessIncome extends React.Component {
  render () {
    return (
      <Consumer>
        { value => (<IncomeTrend userInfo={value} />)} {/*通过属性获取变量*/}
      </Consumer>
    )
  }
}
// 2. 直接在需要变量的组件中放回调方法,在回调方法里面获取变量
// BusinessIncome.jsx 
class ShowIncomeTable extends React.Component {
  consumerCallback = (value) => {
    console.log('consumerCallback -> value', value);
  }
  render () {
    return (
      <Consumer>
        <div>
          <Consumer>{this.consumerCallback}</Consumer>
        </div>
      </Consumer>
    )
  }
}

参考资料:http://react.yubolun.com/docs/context.html

5. setState()说明

setState()将对组件状态的改变排队,并告诉该组件及其子组件需要用已经更新的状态来重新渲染。这个方法主要是用来更新用户界面以响应事件处理和服务器响应。

1、setState()不总是立刻更新组件。其可能是成批处理或推迟更新。这使得在调用setState()后立刻读取this.state变成一个潜在陷阱。

this.setState({ tt: 20}, () => { console.log('tt: ', this.state.tt) });
this.setState({ tt: 50}, () => { console.log('tt: ', this.state.tt) });

上面代码执行后,输出50 50, 说明方法合并到一起执行了

2、setState()永远都会导致重新渲染,除非shouldComponentUpdate() 返回false。所以和渲染无关的状态不要放在state中。

3、setState()的第二个参数是一个可选的回调函数,其执行将是在一旦setState完成,并且组件被重新渲染之后。通常,对于这类逻辑,我们推荐使用componentDidUpdate

由于使用setState()更新了值后,不会立刻就能使用this.state看到最新的值,如:

constructor(props, context) {
  super(props, context)
  this.state = { value: 1 };
}
// 第一次render后的生命周期
componentDidMount () {
  let value = 10;
  this.setState({tt: 10,})
  console.log('tt: ', this.state.tt);	// 此时输出的是1
}

5.1 执行setState()后this.state立即获取到更新方法

setState()的第二个参数是一个可选的回调函数,其执行将是在一旦setState完成,并且组件被重新渲染之后。可用如下方法:

componentDidMount () {
  this.setState({ tt: 20}, () => {
    this.pp();
  });
  // this.setState({tt: 30}, this.pp ) 此方法也可以
}
pp = () => { console.log('tt: ', this.state.tt) }

可以使用setTimeout异步函数来替代。

componentDidMount () {
 this.setState({tt: 40})
 setTimeout(this.pp, 0);	// 40
}
pp = () => { console.log('tt: ', this.state.tt) }

或者把需要的实时的变量放到类变量里面:

constructor(props, context) {
  super(props, context)
  this.state = { value: 1 };
  this.value = 1; // 直接把变量放在这里
}

参考资料:

https://www.cnblogs.com/feiyu6/p/9202873.html

https://react.docschina.org/docs/react-component.html#setstate

5.2 在setState里面使用state的变量

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

6. 无状态组件SFC

无状态组件顾名思义就是没有状态的组件,如果一个组件不需要管理 state 只是纯的展示,那么就可以定义成无状态组件。无状态组件是在 React 在 v0.14 之后推出的

无状态组件是没有 refs 属性的。

无状态的函数创建的组件是无状态组件,它是一种只负责展示的纯组件:

function HelloComponent(props) {
    return <div>Hello {props.name}</div>
}
ReactDOM.render(<HelloComponent name="marlon" />, mountNode)

对于这种无状态的组件,使用函数式的方式声明,会使得代码的可读性更好,并能大大减少代码量,箭头函数则是函数式写法的最佳搭档:

const Todo = (props) => (
  <li
    onClick={props.onClick}
    style={{textDecoration: props.complete ? "line-through" : "none"}}
  >
    {props.text}
  </li>
)

上面定义的 Todo 组件,输入输出数据完全由props决定,而且不会产生任何副作用。对于propsObject 类型时,我们还可以使用 ES6 的解构赋值:

const Todo = ({ onClick, complete, text, ...props }) => (
  <li
    onClick={onClick}
    style={{textDecoration: complete ? "line-through" : "none"}}
    {...props}
  >
    {props.text}
  </li>
)

优点

  • 适当减少代码量,可读性增强;

  • 无状态,统一移交给高阶组件(HOC)或者 Redux 进行管理;

    这种模式在大型项目或者组件中经常被使用,未来 React 也会对 SFC 做一些专门的优化;

这种模式被鼓励在大型项目中尽可能以简单的写法 来分割原本庞大的组件,而未来 React 也会面向这种无状态的组件进行一些专门的优化,比如避免无意义的检查或内存分配。所以建议大家尽可能在项目中使用无状态组件。

无状态组件内部其实是可以使用ref功能的,虽然不能通过this.refs访问到,但是可以通过将ref内容保存到无状态组件内部的一个本地变量中获取到。

例如下面这段代码可以使用ref来获取组件挂载到DOM中后所指向的DOM元素:

function TestComp(props){
  let ref;
  return ( <div ref={(node) => ref = node}></div> )
}

参考资料:

https://www.w3cplus.com/react/stateful-vs-stateless-components.html

7. PureComponent 纯组件

PureComponent 的作用:用来提升性能,因为它减少了应用中的渲染次数。

React15.3 中新加了一个 PureComponent 类,它是优化 React 应用程序最重要的方法之一。

在简单组件(纯展示组件)上的性能可以高出 React.Component 几十倍,所以性能还是很可观的~

7.1 原理

当组件更新时,如果组件的 propsstate 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的「生成」和「比对」过程,达到提升性能的目的。

React 做了如下判断:

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps)
  || !shallowEqual(inst.state, nextState);
}

这里的 shallowEqual 会比较 Object.keys(state | props)长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。

7.2 注意点

  1. 如果 PureComponent 里有 shouldComponentUpdate 函数的话,React 会直接使用 shouldComponentUpdate 的结果作为是否更新的依据;

    只有不存在 shouldComponentUpdate 函数,React 才会去判断是不是 PureComponent,是的话再去做 shallowEqual 浅比较。

    也因为可以少写 shouldComponentUpdate 函数,倒也节省了点代码。

  2. 因为只做了浅比较,所以需要注意 state 或 props 中修改前后的对象引用是否一致;

  3. 由于是 React15.3 之后才有的,所以可能需要进行兼容操作;

    import React { PureComponent, Component } from 'react';
    class Foo extends (PureComponent || Component) {
      //...
    }
    

参考资料:https://blog.lbinin.com/frontEnd/React/React-SFC.html

react官网关于PureComponent组件介绍:https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent

8. Fragments:解决必须有一个根节点问题

<React.Fragment>是为了解决render函数必须有一个跟节点问题。

react版本15以前,render函数的返回必须有一个根节点,否则报错,为满足这一原则我会使用一个没有任何样式的 div 包裹一下。

react版本16开始,render支持返回数据,这一特性已经可以减少不必要节点嵌套:

import React from 'react';
export default function () {
    return [ <div>一步 01</div>, <div>一步 02</div>, <div>一步 03</div> ];
}

如果你不喜欢用数组,React 16为我们提供了Fragments:

import React from 'react';
export default function () {
  return (
    <React.Fragment>
      <div>一步 01</div>
      <div>一步 02</div>
    </React.Fragment>
  );
}

8.1 Fragments简写形式<></>

<></>形式,前有些前端工具*支持的还不太好*,用 create-react-app 创建的项目就不能通过编译

import React from 'react';
export default function () {
  return (
    <>
    <div>一步 01</div>
    <div>一步 02</div>
    </>
  );
}

9 react中DOM元素

https://zh-hans.reactjs.org/docs/dom-elements.html

React 实现了一套独立于浏览器的 DOM 系统,兼顾了性能和跨浏览器的兼容性。我们借此机会完善了浏览器 DOM 实现的一些特殊情况。

在 React 中,所有的 DOM 特性和属性(包括事件处理)都应该是小驼峰命名的方式。例如,与 HTML 中的 tabindex 属性对应的 React 的属性是 tabIndex。例外的情况是 aria-* 以及 data-* 属性,一律使用小写字母命名。比如, 你依然可以用 aria-label 作为 aria-label

9.1 dangerouslySetInnerHTML

使用代码直接设置 HTML 存在风险,因为很容易无意中使用户暴露于跨站脚本(XSS)的攻击。

在react中html字符串不会渲染成真正的html,也不会执行脚本文件。

dangerouslySetInnerHTML 是 React 为浏览器 DOM 提供 innerHTML 的替换方案。当你想设置 dangerouslySetInnerHTML 时,需要向其传递包含 key 为 __html 的对象,以此来警示:

function MyComponent(item) {
  const replaceHtml = (str) => { return str.replace('/', '')}
  // return <div dangerouslySetInnerHTML={createMarkup()} />;
  // <div dangerouslySetInnerHTML={{ __html: '<div>123</div>' }} />
  return <div dangerouslySetInnerHTML={{__html:replaceHtml(item.process)}} />
}

1、dangerouslySetInnerHTMl 是React标签的一个属性。2、有2个{{}},第一{}代表jsx语法开始,第二个是代表dangerouslySetInnerHTML接收的是一个对象键值对。3、.既可以插入DOM,又可以插入字符串。

Last Updated: 8/16/2021, 10:57:48 AM