React組件的state和props


React組件的state和props

React的數據是自頂向下單向流動的,即從父組件到子組件中,組件的數據存儲在propsstate中。實際上在任何應用中,數據都是必不可少的,我們需要直接的改變頁面上一塊的區域來使得視圖的刷新,或者間接地改變其他地方的數據,在React中就使用propsstate兩個屬性存儲數據。

描述

state的主要作用是用於組件保存、控制、修改自己的可變狀態。state在組件內部初始化,可以被組件自身修改,而外部不能訪問也不能修改,可以認為state是一個局部的、只能被組件自身控制的數據源,state中狀態可以通過this.setState方法進行更新,setState會導致組件的重新渲染。
props的主要作用是讓使用該組件的父組件可以傳入參數來配置該組件,它是外部傳進來的配置參數,組件內部無法控制也無法修改,除非外部組件主動傳入新的props,否則組件的props永遠保持不變。
stateprops都可以決定組件的行為和顯示形態,一個組件的state中的數據可以通過props傳給子組件,一個組件可以使用外部傳入的props來初始化自己的state,但是它們的職責其實非常明晰分明,state是讓組件控制自己的狀態,props是讓外部對組件自己進行配置。簡單來說props是傳遞給組件的(類似於函數的形參),而state是在組件內被組件自己管理的(類似於在一個函數內聲明的變量)。
一個清晰的原則是盡量少地用state,盡量多地用props,沒有state的組件叫無狀態組件stateless component,設置了state的叫做有狀態組件stateful component。因為狀態會帶來管理的復雜性,我們盡量多地寫無狀態組件,盡量少地寫有狀態的組件,這樣會降低代碼維護的難度,也會在一定程度上增強組件的可復用性。

props

React的核心思想就是組件化思想,頁面會被切分成一些獨立的、可復用的組件。組件從概念上看就是一個函數,可以接受一個參數作為輸入值,這個參數就是props,所以可以把props理解為從外部傳入組件內部的數據,由於React是單向數據流,所以props基本上也就是從服父級組件向子組件傳遞的數據。
假設我們現在需要實現一個列表,我們把列表中的行當做一個組件,也就是有這樣兩個組件<ItemList/><Item/>。列表ItemList組件的數據我們就暫時先假設是放在一個data變量中,然后通過map函數返回一個每一項都是<Item item={數據}/>的數組,也就是說這里其實包含了data.length<Item/>組件,數據通過在組件上自定義一個參數傳遞。之后在Item組件內部是使用this.props來獲取傳遞到該組件的所有數據,它是一個對象其中包含了所有對這個組件的配置,現在只包含了一個item屬性,所以通過this.props.item來獲取即可。

// Item組件
class Item extends React.Component{
  render(){
    return (
      <li>{this.props.item}</li>
    )
  }
}

// ItemList組件
class ItemList extends React.Component{
  render(){
    const data = [1, 2, 3, 4, 5, 6];
    const itemList = data.map((v, i) => <Item item={v} key={i}/>);
    return (
      <ul>{itemList}</ul>
    )
  }
}

props經常被用作渲染組件和初始化狀態,當一個組件被實例化之后,它的props是只讀的,不可改變的。如果props在渲染過程中可以被改變,會導致這個組件顯示的形態變得不可預測,只有通過父組件重新渲染的方式才可以把新的props傳入組件中。也就是說props是一個從外部傳進組件的參數,主要作為就是從父組件向子組件傳遞數據,它具有可讀性和不變性,只能通過外部組件主動傳入新的props來重新渲染子組件,否則子組件的props以及展現形式不會改變。
在組件中,我們也可以為props中的參數設置一個defaultProps,並且制定它的類型。

import PropTypes from "prop-types";

class Hello extends React.Component{
  constructor(props){
    super(props);
  }
  render() {
    return (
      <div>{this.props.tips}</div>
    );
  }
}

Hello.propTypes = {
  tips: PropTypes.string
};

不同的驗證器類型如下。

import PropTypes from "prop-types";

MyComponent.propTypes = {
  // JS原始類型,這些全部默認是可選的
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 可以直接渲染的任何東西,可以是數字、字符串、元素或數組
  optionalNode: PropTypes.node,

  // React元素
  optionalElement: PropTypes.element,

  // 指定是某個類的實例
  optionalMessage: PropTypes.instanceOf(Message),

  // 可以是多個值中的一個
  optionalEnum: PropTypes.oneOf(["News", "Photos"]),

  // 可以是多種類型中的一個
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 只能是某種類型的數組
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 具有特定類型屬性值的對象
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 具有相同屬性值的對象
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // 必選條件,可以配合其他驗證器,以確保在沒有提供屬性的情況下發出警告
  requiredFunc: PropTypes.func.isRequired,

  // 必選條件,提供的屬性可以為任意類型
  requiredAny: PropTypes.any.isRequired,

  // 自定義 oneOfType 驗證器。如果提供的屬性值不匹配的它應該拋出一個異常
  // 注意:不能使用 console.warn 和 throw
  customProp: function(props, propName, componentName) {
    if (!/matcher/.test(props[propName])) {
      return new Error("Not Match");
    }
  },

  // 自定義 arrayOf 或者 objectOf 驗證器。
  // 它會調用每個數組或者對象的key,參數前兩個是對象它本身和當前key
  // 注意:不能使用 console.warn  和 throw
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matcher/.test(propValue[key])) {
      return new Error("Not Match");
    }
  })
};

state

一個組件的顯示形態可以由數據狀態和外部參數所決定,外部參數也就是props,而數據狀態就是statestate的主要作用是用於組件保存、控制以及修改自己的狀態,它只能在constructor中初始化,它算是組件的私有屬性,不可通過外部訪問和修改,只能通過組件內部的this.setState來修改,修改state屬性會導致組件的重新渲染。簡單來說就是在組件初始化的時候,通過this.state給組件設定一個初始的state,在第一次render的時候就會用這個數據來渲染組件。

class Hello extends React.Component{
  constructor(props){
    super(props);
    this.state = { 
      tips: "Hello World!"
    }
  }
  render() {
    return (
      <div>{this.state.tips}</div>
    );
  }
}

state不同於props的一點是,state是可以被改變的。不過不可以直接通過this.state= values;的方式來修改,而需要通過this.setState()方法來修改state。例如我們經常會通過異步操作來獲取數據,我們需要在didMount生命周期階段來執行異步操作。

componentDidMount(){
  fetch("url")
    .then(response => response.json())
    .then((data) => {
      this.setState({itemList:item});  
    }
}

當我們調用this.setState方法時,React會更新組件的數據狀態state,並且重新調用render方法,也就是會對組件進行重新渲染。setState接受一個對象或者函數作為第一個參數,只需要傳入需要更新的部分即可,setState還可以接受第二個參數,它是一個函數,會在setState調用完成並且組件開始重新渲染時被調用,可以用來監聽渲染完成。

this.setState({ tips: "data update" });
this.setState({ tips: "data update" }, () => console.log("finished"));

示例

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>React</title>
</head>

<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
  class Item extends React.Component{
    render(){
      return (
        <li>{this.props.item}</li>
      )
    }
  }

  class ItemList extends React.Component{
      render(){
        const data = [1, 2, 3, 4, 5, 6];
        const itemList = data.map((v, i) => <Item item={v} key={i}/>);
        return (
          <ul>{itemList}</ul>
      )
    }
  }

  class Hello extends React.Component{
    constructor(props){
      super(props);
      this.state = { 
        tips: "Hello World!"
      }
    }
    render() {
      return (
        <div>{this.state.tips}</div>
      );
    }
  }

  class App extends React.Component{
    render() {
      return (
        <div>
            <Hello />
            <ItemList />
        </div>
      );
    }
  }

  var vm = ReactDOM.render(
    <App />,
    document.getElementById("root")
  );
</script>

</html>

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://github.com/axuebin/react-blog/issues/8
https://zh-hans.reactjs.org/docs/faq-state.html
http://huziketang.mangojuice.top/books/react/lesson12


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM