react的一些小概念


2019-11-12 react

状态提示

在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。

虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入

总结一下:当某个状态被多个组件依赖或者影响的时候,就把该状态提升到这些组件的最近公共父组件中去管理,用 props 传递数据或者函数来管理这种依赖或着影响的行为。

组合 vs 继承

React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用

有些组件无法提前知晓它们子组件的具体内容。在 Sidebar(侧边栏)和 Dialog(对话框)等展现通用容器(box)的组件中特别容易遇到这种情况。

我们建议这些组件使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中

//父组件
function FancyBorder(props) {
  return (
    <div className={'box-color' + props.color}>
      {props.children}
    </div>
  );
}
1
2
3
4
5
6
7
8
//子组件
function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        我是子组件
      </h1>
      <p className="Dialog-message">
        通过组合的方式嵌入到父组件中,父组件可以通过this.props.children来找到
      </p>
    </FancyBorder>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13

如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们

PropTypes

利用 ES7 的静态属性关键字 static

class PropTypeTwo extends React.Component {
  static propTypes = {
      name:PropTypes.string
  };
  render() {
    return (
      <div>
        <div>{this.props.name}</div>
      </div>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

state 值改变的五种方式

方式 1

let {count} = this.state
this.setState({count:2})
1
2

方式 2 :callBack

this.setState(({count})=>({count:count+2}))
1

方式 3 :接收 state 和 props 参数

this.setState((state, props) => {
    return { count: state.count + props.step };
});
1
2
3

方式 4:hooks

const [count, setCount] = useState(0)
// 设置值
setCount(count+2)
1
2
3

方式 5:state 值改变后调用

this.setState(
    {count:3},()=>{
        //得到结果做某种事
    }
)
1
2
3
4
5

监听states 变化

1.16.x 之前使用componentWillReveiveProps

componentWillReceiveProps (nextProps){
  if(this.props.visible !== nextProps.visible){
      //props 值改变做的事
  }
}
1
2
3
4
5

注意:有些时候componentWillReceiveProps在 props 值未变化也会触发,因为在生命周期的第一次render后不会被调用,但是会在之后的每次render中被调用 = 当父组件再次传送props

2.16.x 之后使用getDerivedStateFromProps,16.x 以后componentWillReveiveProps也未移除

export default class Six extends React.Component {
  state = {
    countOne:1,
    changeFlag:''
  };
  clickOne(){
    let {countOne} = this.state
    this.setState({countOne:countOne+1})
  };
  static getDerivedStateFromProps (nextProps){
    console.log('变化执行')
    return{
      changeFlag:'state 值变化执行'
    }
  }
  render() {
    const {countOne,changeFlag} = this.state
    return (
      <div>
        <div>
         <Button type="primary" onClick={this.clickOne.bind(this)}>点击加 1</Button><span>countOne 值为{countOne}</span>
        <div>{changeFlag}</div>
        </div>
      </div>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

高阶组件

如果一个函数 接受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数。

下面就是一个简单的高阶函数:

function withGreeting(greeting = () => {}) {
    return greeting;
}
1
2
3

高阶组件 的定义和 高阶函数 非常相似:

如果一个函数 接受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件。

下面就是一个简单的高阶组件:

function HigherOrderComponent(WrappedComponent) {
    return <WrappedComponent />;
}
1
2
3

所以你可能会发现,当高阶组件中返回的组件是 无状态组件(Stateless Component) 时,该高阶组件其实就是一个 高阶函数,因为 无状态组件 本身就是一个纯函数。

无状态组件也称函数式组件。

React 中的高阶组件

就是类似高阶函数的定义,将组件作为参数或者返回一个组件的组件

1.属性代理

import React,{Component} from 'react';

const Seventeen = WrappedComponent =>
  class extends React.Component {
    render() {
      const props = {
        ...this.props,
        name: "这是高阶组件"
      };
      return <WrappedComponent {...props} />;
    }
  };

class WrappedComponent extends React.Component {
  state={
     baseName:'这是基础组件' 
  }
  render() {
    const {baseName} = this.state
    const {name} = this.props
    return <div>
        <div>基础组件值为{baseName}</div>
        <div>通过高阶组件属性代理的得到的值为{name}</div>
    </div>
  }
}

export default Seventeen(WrappedComponent)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

2.反向继承

  const Seventeen = (WrappedComponent)=>{
    return class extends WrappedComponent {
        componentDidMount() {
            this.setState({baseName:'这是通过反向继承修改后的基础组件名称'})
        }
        render(){
            return super.render();
        }
    }
}

class WrappedComponent extends React.Component {
  state={
     baseName:'这是基础组件' 
  }
  render() {
    const {baseName} = this.state
    return <div>
        <div>基础组件值为{baseName}</div>
    </div>
  }
}

export default Seventeen(WrappedComponent);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

路由传参

1.params

<Route path='/path/:name' component={Path}/>
<link to="/path/2">xxx</Link>
this.props.history.push({pathname:"/path/" + name});
读取参数用:this.props.match.params.name
1
2
3
4

2.query

<Route path='/query' component={Query}/>
<Link to={{ path : '/query' , query : { name : 'sunny' }}}>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
读取参数用: this.props.location.query.name
1
2
3
4

3.state

<Route path='/sort ' component={Sort}/>
<Link to={{ path : '/sort ' , state : { name : 'sunny' }}}> 
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});
读取参数用: this.props.location.query.state 
1
2
3
4
<Route path='/web/search ' component={Search}/>
<link to="web/search?id=12121212">xxx</Link>
this.props.history.push({pathname:`/web/search?id ${row.id}`});
读取参数用: this.props.location.search
1
2
3
4

5.优缺点

1.params和 search 只能传字符串,刷新页面参数不会丢
2.query和 state 可以传对象,但是刷新页面参数会丢失
1
2
Thomas: 12/2/2019, 1:58:58 PM