React飛行日記(九) - 複合元件互動

菜鳥怪物發表於2020-10-11

-父子元件互動

1.狀態和方法

React中父子元件的互動有兩種方式(除去redux)

第一種是靠(狀態|屬性)完成

  • 父傳子:(父元件把其狀態,作為子元件屬性傳入,父狀態改變子元件重渲染)【父->子】
  • 子傳父:(父元件把其方法,作為子元件屬性傳入,子元件呼叫方法影響父元件)【子->父】

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-hUYpOGQ7-1602418368850)(React.assets/.png)]

2.context上下文

第二種是靠(狀態|上下文context)完成

上下文和屬性的不同點:

  1. 父元件定義上下文,所有後代元件都能使用。(屬性只停留在父子元件層面)
  2. 每個後代元件中的上下文都是獨立的,修改了值並不會影響其他元件的上下文。(屬性不能修改)
  • 如何進行上下文傳遞?

    1. 在父元件中宣告上下文

    2. 在後代元件中使用

  • 為什麼後代元件也能使用父元件的上下文?

    因為上下文是通過父元件中的子元件的私有屬性一級一級傳遞下去的

    每個子元件都有兩個私有屬性

    1. __reactInternalMemoized**Masked**ChildContext 當前元件使用的上下文
    2. __reactInternalMemoized**Unmasked**ChildContext 子元件暴露給後代元件的上下文

    當我在子元件中對上下文的內容進行修改時候
    ,實際上修改的是__reactInternalMemoizedMaskedChildContext __ 中的內容。

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-YlaYBzR2-)(React.assets/.png)]

    但是當前子元件的後代元件中的上下文是受當前子元件的 reactInternalMemoizedUnmaskedChildContext 控制的,所以無論怎麼修改當前元件上下文的的內容都不會影響下面一層元件的上下文,都可以共享父元件的上下文,比屬性好

上下文代替屬性

地址:https://github.com/Adermi/text

-平行元件互動

1.套用共同父元件

在兩個平行元件上包一個父元件,父元件的狀態|方法傳入子元件,子元件呼叫方法改變父元件的狀態,父元件狀態改變,重新渲染子元件

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-xWl4GOuc-1602418368855)(D:\Code\筆記\JS\React.assets)]

上面提到了 父影響子 子影響父 ,那麼平行元件之間要怎麼互相影響,比如:

​ 點選元件A中的按鈕,如何影響B元件中的計數值?

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-56XaucV6-1602418368857)(React.assets/.png)]

常用的方法就是:在A和B元件外套一個共同的Father父元件

  • 思路:

    1. 建立一個父元件的狀態 times,作為屬性傳入子元件A,作為點選次數
    2. 建立兩個父元件的方法 addreduce,作為屬性傳入子元件B,作為點選事件的回撥
    3. 這樣,子元件B的屬性中就有兩個方法,分別把這兩個按鈕的onClick事件繫結addreduce 回撥

    實現:點選子元件B中的按鈕,觸發父元件add|reduce,方法內會對父元件狀態times進行修改,父組的件狀態times改變,子元件A的屬性就會改變,從而React會進行重新渲染

*案例:加減點選
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css'

let root = document.querySelector('#root')

class Head extends React.Component {
  constructor() {
    super()
  }

  render() {
    let { times } = this.props
    return <div className='panel-heading'>
      <h3 className='panel-title'>
        點選次數: {times}</h3>
    </div>
  }
}

class Body extends React.Component {
  constructor() {
    super()
  }

  render() {
    let { addTimes, reduceTimes } = this.props.callback
    return <div className='panel-body'>
      <button className='btn btn-success' onClick={addTimes}>增加</button>
      &emsp;
      <button className='btn btn-danger' onClick={reduceTimes}>減少</button>
    </div>
  }
}

class Panel extends React.Component {
  constructor() {
    super()
    this.state = {
      times: 0
    }
  }

  addTimes = (...args) => {
    this.setState({
      times: ++this.state.times
    })
  }

  reduceTimes = (...args) => {
    if (this.state.times > 0) {
      this.setState({
        times: --this.state.times
      })
    }
  }

  render() {
    let { times } = this.state
    let myStyle = {
      width: '16%',
      margin: '50px auto'
    }
    return <section className='panel panel-default' style={myStyle}>
      <Head times={times}></Head>
      <Body callback={{ addTimes: this.addTimes, reduceTimes: this.reduceTimes }}></Body>
    </section>
  }

}

ReactDOM.render(
  <Panel></Panel>,
  root
);

2.Redux

redux是用來統一管理元件狀態的容器,通過createStore(引數reducer:狀態管理員)建立一個store容器,該容器提供幾個方法:

  1. getState: 返回redux管理的所有的狀態值
  2. subscribe: 用來訂閱事件,當store.dispatch被執行,通知該事件池中的所有方法執行
  3. dispatch: 傳遞標識許可權符號,通知reduxer(狀態管理員)修改狀態,並通知方法執行

redux的應用場景:

  1. 多個元件之間實現資訊共享,共享的資訊(狀態)儲存到redux中
  2. 使用redux做臨時儲存(或者用localstorage),頁面不重新整理,元件就不必重新從伺服器請求資料
執行流程圖

工程化目錄
  1. src目錄下新建資料夾store

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-gLrZ9qh5-1602418368860)(React.assets/.png)]

  2. 目錄功能

    /**
     * store
     *    reducer 存放每一個模組的reducer
     *      -firstCommponent.js   第一個模組的reducer
     *      -secondCommponent.js  第二個元件的reducer
     *      -......
     *      -index.js   把每一個模組的reducer最後合併成一個reducer
     * 
     *    action  存放每一個模組需要進行的派發任務(ActionVreator)
     *      -firstCommponent.js   第一個模組的派發任務
     *      -secondCommponent.js  第二個元件的派發任務
     *      -......
     *      index.js    所有模組的行為派發的合併
     * 
     *    action-types.js   所有派發任務的行為標識都在這裡進行集中管理
     *    index.js   建立store容器
     * 
     */
    
  3. 工程化reducer的編寫

    github:https://gitee.com/andermi/the_use_of_redux_react

3.react-redux

react-redux是基於react量身定製的redux庫,簡化了redux的很多的操作,提供了兩個重要的元件:provider和connect,內部還幫我們進行事件池subscribe訂閱,目錄結構和redux保持一致

程式碼編寫
  1. provider:整個專案需要放在該根元件下,內部通過上下文共享store

    import { Provider} from 'react-redux'
    import store from './store/index'
    import Counter from './conponent/Counter/Counter'
    
    let root = document.querySelector('#root')
    
    ReactDOM.render(
      <Provider store={store}>
        {/* Provider中只能放一個標籤,否則報錯 */}
        <section>
          <Counter></Counter>
        </section>
      </Provider>,
      root
    )
    
  2. connect:高階元件,把當前元件進行封裝,並把redux中的狀態和行為派發都繫結到元件上

    // 寫法
    /** 
     *	以往我們寫元件都是export default class ComA extends React.Component {...}
     *		使用高階元件connect時候,不需要這樣寫,具體參照下面寫法
     *				mapStateToProps: 回撥函式,把redux中的狀態繫結到該元件屬性上
     *				mapDispatchToProps:回撥函式,把redux中的行為派發繫結到該元件屬性上,
     *														並自動完成dispatch繫結和subscribe事件池追加
     */
    
    class Counter extends React.Component {
      UNSAFE_componentWillMount() {
        this.props.init({
          name: 'xyb is best',
          age: '20'
        })
      }
    
      render() {
            let { add, reduce } = this.props
        return 
        <div className={' panel-body'}>
          <button className={'btn btn-success'} onClick={add}>增加</button>&emsp;
          <button className={'btn btn-danger'} onClick={reduce}>減少</button>
        </div>
        <div className={' panel-footer'}>
          {this.props.name}
        </div>
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(Counter)
    
    //分割//
    // 兩個回撥函式 mapStateToProps 與 mapDispatchToProps 的寫法
    
    ---------------寫法一----------------
    let mapStateToProps = state => {
      // => state: Redux容器中的狀態資訊
      // => 在這個函式中返回的是啥,當前元件的屬性上掛載的就是啥(Redux中儲存了很多元件的狀態,所以我們要進行資料篩選)
      return {...state.counter}
    }
    
    let mapDispatchToProps = dispatch => {
      // => dispatch: Store中儲存的Dispatch方法,用來觸發事件池的函式
      // => 我們可以直接用這裡的dispatch構建派發任務,然後這個方法會把派發任務掛載到當前元件的props中,我們直接在元件中呼叫就能直接派發任務,並且這個方法會自動向事件池中subscribe進行訂閱方法,只要有狀態改變,就通知對應的元件進行資料修改
      return {
        init(initData) {
          dispatch(action.counter.init(initData))
        }
      }
    }
    
    ---------------寫法二----------------
    export default connect(state => ({...state.counter}), action.counter)(Counter)
    // => react-redux幫我們把action-create中編寫返回action物件的方法自動構建成dispatch派發任務,也就是mapDispatchToProps裡面這樣的寫法,我們只需要傳入一個物件(裡面是返回action函式的物件),他會自動幫我們進行繫結到this.props上面
    

    自動繫結dispath行為派發

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-8ke3BzDM-1602418368862)(React.assets/.png)]

相關文章