美文网首页
4-组件与 prop、state

4-组件与 prop、state

作者: 谷子多 | 来源:发表于2018-04-16 20:42 被阅读0次

语法:

  1. 引入Component
  2. 新建一个类,继承Component
  3. 实现render函数
  4. 在render函数内返回一个jsx,并导出
  5. 入口文件引入该组件
  6. 引入时的名字作为组件标签
import React,{Component} from 'react';
export default class hello extends Component{
  render(){
    return (
      <div>
        <h1>组件语法学习</h1>
      </div>
    )
  }
}
----------------------------------------------------------------------
import Hello from './component/component';

ReactDOM.render(
    <div>
        <Hello></Hello>
        <p
            style={{
                border : '1px solid #000' 

            }}
        >hell,react
        </p>
    </div>,
    document.getElementById('root')
)

prop

ReactDOM.render(
    <div>
        <Hello name='小明' age='18' hubby='学习react'>
            <p>我是children</p>
        </Hello>
        <p
            style={{
                border : '1px solid #000' 

            }}
        >hell,react
        </p>
    </div>,
    document.getElementById('root')
)
import React,{Component} from 'react';

export default class hello extends Component{
  render(){
    let {name,age,hubby} = this.props
    return (
      <div>
        <h1>组件语法学习</h1>
        <p>姓名:{name}</p>
        <p>年龄:{age}</p>
        <p>爱好:{hubby}</p>
        {this.props.children}
      </div>
    )
  }
}

props.children

查看如下代码:

ReactDOM.render(
  <div>
    <Ment num={9} name="Moli">
      <p>网速太卡了!!</p>
    </Ment>

  </div>

  , document.getElementById('root')
);

我们在使用 Ment 组件的时候, 我们在标签之间插入了一个 p 标签, 但是你会发现 p 标签的内容并没有渲染到页面上. 💢💢

事实上, 以下两种写法是等价的: 🌟🌟

<Ment num={9} name="Moli">
  <p>网速太卡了!!</p>
</Ment>

// === 这两种写法等价

<Ment
  num={9}
  name="Moli"
  children={<p>网速太卡了!!</p>}
/>

发现了么, 写在组件标签之间的内容本质上只是给组件传递了一个 children 属性.

所以, 要想 p 标签显示出来, 就得用上它.

比如: 在组件实现上, 你可以这样:

function Ment(props) {
  return (
      <div>
        <h2>Hello {props.name}</h2>
        {props.num} people here!
        {props.children}
      </div>
  )
}

state: 内部状态

确定一个共识: 类组件才有内部状态!

一个组件在某些时候需要作出一些改变. 它不可能总是一成不变的.

比如一个计数器, 在某一次点击过后, 现实的数字就会改变; 再比如一个时钟程序, 现实的数字随着时间变动.

现在我们尝试做一个计数器看看.

这是我们的基础代码:

import React from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component{

  render(){
    return (
      <div>
        <p>目前计数: 0</p>
        <button>计数 +1</button>
      </div>
    )
  }
}

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

Counter 组件现在渲染之后是这个样子的:

image.png

给组件添加状态

现在我们改造一下 Counter 组件:

class Counter extends React.Component{

  constructor(props){
    super(props);
    this.state= {
      count: 0
    };
  }

  render(){

    let { count } = this.state;

    return (
      <div>
        <p>目前计数: {count}</p>
        <button>计数 +1</button>
      </div>
    )
  }
}

定义状态

我们首先增加了构造函数 constructor():

constructor(props){
  super(props);
  this.state = {
    count: 0
  };
}

构造函数接收的第一个参数就是我们之前学的 props;

我们先使用 super(props); 把组件的 props 传给父类的构造函数, 否则在构造函数里, 即便传递了 props, this.props 的值也会是 undefined.

最关键的:

我们组件的实例上赋予了一个 state 变量. 并让它的值是一个对象.

state 的值, 要么是一个对象: {}, 要么是 null.

这样, 组件便有了一个内部状态.

使用状态

在类的任何地方, 我们都可以通过组件实例拿到这个状态并使用.

比如在 render() 方法里面:

render(){

  let { count } = this.state;

  return (
    <div>
      <p>目前计数: {count}</p>
      <button>计数 +1</button>
    </div>
  )
}

我们访问了 this.state.count 的值, 并渲染了它.

改变状态

现在我们想做一件事情, 点击按钮, count 的值就 +1.

我们需要先给按钮添加一个点击事件:

render(){

    let { count } = this.state;

    return (
      <div>
        <p>目前计数: {count}</p>
        <button
          onClick={()=>{
            this.setState({
              count: count +1
            })
          }}
        >计数 +1</button>
      </div>
    )
  }

现在你点击按钮, 就会发现数字会出现变化.

这里有一些关键点:

添加事件我们会在后面详细说, 现在简单说一下, 给元素一个 onClick 的属性, 就添加了点击事件, 事件接收一个回调函数.

要想改变 count 的值, 你不能直接修改 this.state, 而应该使用组件实例的 this.setState() 接口.
另外, 如果你的 state 属性很多, 比如:

state= {
  count: 0,
  c1: 0,
  c2: 0
};

如果你只想改变 c1 的值, 那么只需:

this.setState({
  c1: 2
})

就可以了.

何为内部状态

如果你渲染多个 Counter 的实例:

ReactDOM.render(
  <div>
    <Counter/>
    <Counter/>
    <Counter/>
  </div>

  , document.getElementById('root')
);

点击不同的按钮, 查看界面:

image.png

你会发现, 组件实例之间的状态互不影响. 这也是为什么我们把组件的 state 称为内部状态.


State 的重要特性

本节的内容说的比较抽象. 你需要使用代码调试来理解.

State 会合并更新

比如你的状态是这样的:

this.state= {
  c1: 0,
  c2: 0
};

你可以这样去只更新其中的一部分:

this.setState({
  c1: 5,
});

this.setState({
  c2: 8,
});

这里就自然地引出了 setState 的第二种使用方式:可以接受一个函数作为参数。React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:

  handleClickOnLikeButton () {
    this.setState((prevState) => {
      return { count: 0 }
    })
    this.setState((prevState) => {
      return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
    })
    this.setState((prevState) => {
      return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
    })
    // 最后的结果是 this.state.count 为 3
  }

上面我们进行了三次 setState,但是实际上组件只会重新渲染一次,而不是三次;这是因为在 React.js 内部会把 JavaScript 事件循环中的消息队列的同一个消息中的 setState 都进行合并以后再重新渲染组件。

深层的原理并不需要过多纠结,你只需要记住的是:在使用 React.js 的时候,并不需要担心多次进行 setState 会带来性能问题。

state 通常是异步更新

如果现在 c1 的值是 0, 然后你更新:

// 现在 c1 = 0

this.setState({
  c1: 2
});

console.log(this.state.c1) // 打印 0

这个时候打印的结果是 0. 原因是调用 this.setState() 后, c1 的值不会立即发生更新.

在异步执行的函数里面, state 会同步更新

正常情况下 :更新是异步的,setState会合并更新。
异步执行的函数里面:setState会同步更新。
setTimeout、异步请求的回调函数(ajax),Promise

要注意性能问题,异步是同步更新的,所以要注意一下state的效率。
比如一个页面有多个数据需要请求,加载一个页面的时候比如有5个图表,请求数据之后渲染图表,如果是不同的接口,就要写多个请求函数,一旦这样,数据就是同步改变。

查看如下代码:

// 现在 c1 = 0

setTimeout(()=>{
  this.setState({
    c1: 2
  });

  console.log(this.state.c1) // 打印 2
})

这个时候结果打印 2, 如果 this.setState() 在一个异步函数里面调用, 那么 state 会立即更新.

以下的函数同样是这种情况:

  • Promise 的回调函数
  • ajax 响应后的回调函数

只要是异步执行的函数, 就适用这种情况.

this.setState() API

setState() 第一个参数可以传入一个对象, 这种使用方式我们已经知道.

第一个参数还可以传入一个函数:

this.setState((preState, props)=>{
  return {
    c1: preState.c1 + 1
  }
});

preState 是之前的 state

props 是组件的 props

函数返回的就是要更新的组件状态.

再看看如下代码:

// c1 此时是 0

this.setState((preState, props)=>{
  console.log(preState.c1); // 打印 0
  return {
    c1: 2
  }
});

console.log(this.state.c1); // 打印 0

this.setState((preState, props)=>{
  console.log(preState.c1); // 打印 2
  return {
    c1: 5
  }
});

console.log(this.state.c1); // 打印 0

仔细查看这几次打印, state 异步更新的情况没有变.

preState 的值是前一次 setState() 调用之后, 得到的 State;

第二个参数

setState() 第二个参数是一个可选的毁掉函数, 当组件更新完成之后, 会调用.

state 的更新只是浅层合并

如果这是现在 state 的情况:

this.state = {
  c1: 0,
  c2: {a:1, b: 2}
}

现在你这样更新:

this.setState({
  c2: {a: 40}
})

这个时候, 最终的 state 会变成这样:

this.state = {
  c1: 0,
  c2: {a:40}
}

c2b 属性不见了. 因为整个对象都会换掉了.

setState() 之后进行 1 个层级的浅层合并.

// setState如果直接传对象,会覆盖掉之前的,如果传callback,就可以获取之前的state
export default class Number extends Component{
    constructor(props){
        super(props)
        this.state = {
            magicNumber : Math.random().toString().slice(2,6),
            a:{
                m1:1,
                m2:2
            }
        }
    }
    changeNumber=()=>{ //更新a的数据
        this.setState((prevState,props)=>{
            return {
                a:{
                    ...prevState.a,
                    m1:2
                }
            }
        })
        this.setState((prevState,props)=>{
            return {
                a:{
                    ...prevState.a,
                    m2:3
                }
            }
        })
      
    }
}

总结

为了使得组件的可定制性更强,在使用组件的时候,可以在标签上加属性来传入配置参数。
组件可以在内部通过 this.props 获取到配置参数,组件可以根据 props 的不同来确定自己的显示形态,达到可配置的效果。
可以通过给组件添加类属性 defaultProps 来配置默认参数。
props 一旦传入,你就不可以在组件内部对它进行修改。但是你可以通过父组件主动重新渲染的方式来传入新的 props,从而达到更新的效果。


我们来一个关于 state 和 props 的总结。

state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState 方法进行更新,setState 会导致组件的重新渲染。

props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

区别:

state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。

  1. state 和 props 有着千丝万缕的关系。它们都可以决定组件的行为和显示形态。一个组件的 state 中的数据可以通过 props 传给子组件,一个组件可以使用外部传入的 props 来初始化自己的 state。但是它们的职责其实非常明晰分明:state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。
<ul className="listWrap">
                {
                    this.state.datas.map((elt,i)=>{
                        return (
                            <LiDom
                            id={elt.id}
                            content={elt.content}
                            deleteValue={this.deleteValue}
                            index={i}
                            ></LiDom>
                        )
                    })
                }
            </ul>
class LiDom extends Component{
    render(){
      let {id,content,deleteValue,index} = this.props
       return (
            <li key={id}>
                <span>{index+1}.{content}</span>
                <button onClick={()=>deleteValue(id)}>删除</button>
             </li>
        )
    }
    // 更新前执行的,所以在没有更新之前
    shouldComponentUpdate(nP,nS){
        return !this.props.content === nP.content
    }
}

如果你觉得还是搞不清 state 和 props 的使用场景,那么请记住一个简单的规则:尽量少地用 state,尽量多地用 props。

  1. 没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。前端应用状态管理是一个复杂的问题,我们后续会继续讨论。

React.js 非常鼓励无状态组件,在 0.14 版本引入了函数式组件——一种定义不能使用 state 组件,例如一个原来这样写的组件:

class HelloWorld extends Component {
  constructor() {
    super()
  }

  sayHi () {
    alert('Hello World')
  }

  render () {
    return (
      <div onClick={this.sayHi.bind(this)}>Hello World</div>
    )
  }
}

用函数式组件的编写方式就是:

const HelloWorld = (props) => {
const sayHi = (event) => alert('Hello World')
return (
  <div onClick={sayHi}>Hello World</div>
)
}

以前一个组件是通过继承 Component 来构建,一个子类就是一个组件。而用函数式的组件编写方式是一个函数就是一个组件,你可以和以前一样通过 <HellWorld /> 使用该组件。不同的是,函数式组件只能接受 props 而无法像跟类组件一样可以在 constructor 里面初始化 state。你可以理解函数式组件就是一种只能接受 props 和提供 render 方法的类组件。

相关文章

  • 4-组件与 prop、state

    语法: 引入Component 新建一个类,继承Component 实现render函数 在render函数内返回...

  • 深入React组件的数据:prop、state

    React组件的数据分为两种,prop和state,无论prop或者state改变,都可能引发组件的重新渲染。 p...

  • React:组件的数据

    React组件的数据分为两种,prop和state,无论prop或者state的改变,都可能引发组件的重新渲染,那...

  • props 和state 的理解

    React组件的数据分为两种,prop和state,无论prop或者state改变,都可能引发组件的重新渲染。 p...

  • react小记 prop和state的区别

    1.prop用于定义外部接口,state用于记录内部状态 2,prop 的赋值在外部世界使用组件时,state的赋...

  • React之State

    State 如何组织数据是程序的最重要问题。Raect组件的数据分为两种:prop和state。无论prop还是s...

  • React prop和state

    React中组织数据的形式有两种,分别是prop和state。 prop prop是组件对外的接口,即是外部传递给...

  • 前端-组件、Prop 和 State

    英文:Linton Ye 译文:郑丰彧https://zhuanlan.zhihu.com/p/41398296 ...

  • 3.组件数据

    React的组件数据主要分为两种: prop state 那么怎么选择数据的类型呢?其实选择的原则很简单,prop...

  • react中pureComponent和Component区别

    为什么要使用pureComponent? 当使用component时,父组件的state或prop更新时,无论子组...

网友评论

      本文标题:4-组件与 prop、state

      本文链接:https://www.haomeiwen.com/subject/ozjkkftx.html