欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解React?的數(shù)據(jù)流和生命周期

 更新時間:2022年08月12日 08:52:24   作者:彭加李  
這篇文章主要介紹了React?的數(shù)據(jù)流和生命周期,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

數(shù)據(jù)流和生命周期

如何處理 React 中的數(shù)據(jù),組件之間如何通信,數(shù)據(jù)在 React 中如何流動?

常用的 React 生命周期方法以及開源項目 spug 中使用了哪些生命周期方法?

數(shù)據(jù)和數(shù)據(jù)流

雖然也有很多靜態(tài)網(wǎng)站,但人們使用的大多數(shù)網(wǎng)站都充滿了隨時間變化的數(shù)據(jù)。

stateprops 是 React 組件處理數(shù)據(jù)和彼此通信的兩種主要方法。

React 提供了兩個數(shù)據(jù)相關的 api:state 和 props。

前面我們已經(jīng)知道,不要直接修改 state,而是使用 setState(updater[, callback])

updater 函數(shù)簽名:(preState, props) => stateChange

Tip: 建議傳遞函數(shù)而非對象。React 16 以前的版本允許傳遞對象而不是函數(shù)給 setState 的第一個參數(shù)。傳遞對象給 state 暗示是同步的,實際卻并非如此,而使用函數(shù),更符合 React 的異步范式,React 會更新,但不保證時間。

筆者測試,下面兩種寫法都生效:

this.setState({
    date: new Date()
})

this.setState(() => ({
    date: new Date()
}))

假設我們想根據(jù) props.step 來增加 state,可以這樣:

this.setState((state, props) => {
  return {counter: state.counter + props.step};
});

setState 是淺合并,而非替換。請看實驗:合并還是替換

如果需要在應用更新后觸發(fā)某些動作,可以使用 setState 的回調(diào)函數(shù)(setState(updater, callback)

不可變狀態(tài) props

在 React 中,屬性是傳遞不可變數(shù)據(jù)的主要方式,所有組件都可以接收屬性,并能在構造函數(shù)、render()和生命周期方法中使用。

屬性通常來自父組件或自身的默認屬性值(defaultProps)。

是否可以將父組件的 state 作為屬性傳給子組件呢?是的。一個組件的狀態(tài)可以是另一個組件的屬性。

可以將任何有效的 js 數(shù)據(jù)作為屬性傳遞給子組件

Tip: 前面 我們已經(jīng)測試過了,如果修改 props(this.props.name = 'aName'),控制臺會報錯。

屬性可以隨時間改變,但不是從組件內(nèi)部改變。這是單向數(shù)據(jù)流的一部分,下面會提到。

只使用 props 的組件

倘若我們希望將狀態(tài)保存在一個中心位置,而不是分散在各個組件中,比如 Flux、Redux等。這時我們可以創(chuàng)建無狀態(tài)函數(shù)組件(或稱之為函數(shù)組件)。

無狀態(tài)不是沒有任何種類的狀態(tài),而是它不會獲得 React 進行管理的支撐實例,意味著沒有生命周期方法,沒有 state。

無狀態(tài)函數(shù)組件與有支撐實例的父組件結(jié)合使用時非常強大。與其在多個組件間設置狀態(tài),不如創(chuàng)建單個有狀態(tài)的父組件并讓其余部分使用輕量級子組件。例如 Redux 會將這個模式提升到全新的水平。

Tip:請練習,使用一個組件的狀態(tài)修改另一個組件的屬性。(下文父子組件有使用)

組件通信

現(xiàn)在我們能夠用子組件輕易的構建新組件,能夠很容易地表示組件間的 has-a、is-a 的關系。如何讓他們通信呢?

在 React 中,如果想讓組件彼此通信,需要傳遞屬性,開發(fā)人員會做兩件事:

  • 訪問父組件中的數(shù)據(jù)(狀態(tài)或?qū)傩裕?/li>
  • 傳遞數(shù)據(jù)給子組件

下面定義 1 個父組件,2 個子組件,其中 2 個子組件中的數(shù)據(jù)來自父組件。請看示例:

const MyComponent = (props) => (
    <div>
        <MyComponentSub1 content={props.cnt1}/>
        <MyComponentSub2 content={props.cnt2}/>
    </div>
)

const MyComponentSub1 = (props) => (
    <p>sub1: {props.content}</p>
)

const MyComponentSub2 = (props) => (
    <p>sub2: {props.content}</p>
)
ReactDOM.render(
    <MyComponent cnt1="apple" cnt2="orange" />,
    document.getElementById('root')
);

頁面顯示:

sub1: apple

sub2: orange

單向數(shù)據(jù)流

數(shù)據(jù)如何流經(jīng)應用的不同部分?

在 React 中,UI 是數(shù)據(jù)投射到視圖中的數(shù)據(jù),當數(shù)據(jù)變化時,視圖隨之變化。

React 中,數(shù)據(jù)流是單向的。上層通過 props 傳遞數(shù)據(jù)給子組件,子組件通過回調(diào)函數(shù)將數(shù)據(jù)傳遞給上層。單向數(shù)據(jù)流讓思考數(shù)據(jù)在應用中的流動變得更簡單

也得益于組件的層次結(jié)構以及將屬性和狀態(tài)局限于組件,預測數(shù)據(jù)在應用中如何移動也更加簡單。

數(shù)據(jù)在 React 中是按一個方向流動的。屬性由父組件傳遞給子組件(從所有者到擁有者),子組件不能編輯父組件的狀態(tài)或?qū)傩浴C總€擁有支撐實例的組件都能修改自身的 state 但無法修改超出自身的東西,除了設置子組件的屬性。

如果允許從應用的部分隨意修改想要修改的東西,聽上去好像不錯,實際上可能會導致難以捉摸的應用并且可能會造成調(diào)試困難。

React 中的數(shù)據(jù)流是單向的,從父組件流向子組件,子組件通過回調(diào)函數(shù)將數(shù)據(jù)回送給父組件,但它不能直接修改父組件的狀態(tài),而父組件也不能直接修改子組件的狀態(tài),組件間通信通過屬性完成。

渲染和生命周期

渲染就是 React 創(chuàng)建和管理用戶界面所做的工作,也就是讓應用展現(xiàn)到屏幕上。

和 vue 中生命周期類似,react 也可以分 4 部分:

  • 初始 - 組件被實例化的時候
  • 掛載 - 組件插入到 dom
  • 更新 - 通過狀態(tài)或?qū)傩杂眯聰?shù)據(jù)更新組件
  • 卸載 - 組件從 dom 中移除

生命周期方法簡介

筆者在前文已經(jīng)分析過一次 react 生命周期相關方法,這里再次總結(jié)一下:

掛載時的順序:constructor()、render()、componentDidMount()(組件掛載后立即調(diào)用)

Tip:

  • 掛載是 React 將組件插入到 dom 的過程。React 在實際 dom 中創(chuàng)建組件之前,組件只存在虛擬 dom 中。
  • 容易犯的一個錯誤是把不該放到 render() 中的東西放到了 render() 里面。render() 通常會在組件的生命周期內(nèi)調(diào)用多次,也無法確定 React 會何時調(diào)用 render(),因為出于性能 React 會批量更新。
  • 掛載(ReactDOM.render)和卸載(ReactDOM.unmountComponentAtNode)由 React.DOM 從外部控制。沒有 ReactDOM 的幫助,組件不能卸載自己。

更新時的順序:shouldComponentUpdate、render()、componentDidUpdate(組件更新后被立即調(diào)用)

卸載時:componentWillUnmount

過時的生命周期有:componentWillMount、componentWillReceiveProps、componentWillUpdate。官網(wǎng)不建議在新代碼中使用它們。

避免使用:forceUpdate、shouldComponentUpdate。

Tip:React 采用了復雜的、先進的方法確定應該更新什么以及何時更新。如果最終使用了 shouldComponentUpdate,則應該是那些方法由于某種原因不夠用的情況下。

新增生命周期方法:getDerivedStateFromProps、getSnapshotBeforeUpdate。都屬于不常見的情形。

spug 使用了哪些生命周期方法

React 有那么多生命周期相關方法,那么像 spug 這種運維相關的開源項目用到了哪些,是否都用到了?

答:只用到了掛載、更新和卸載 3 個方法。

請看筆者統(tǒng)計:

constructor

共 25 處

幾乎都是初始化 state。沒有發(fā)送數(shù)據(jù)請求。摘錄如下:

constructor(props) {
    super(props);
    this.state = {
        groupMap: {}
    }
}
constructor(props) {
    super(props);
    this.textView = null;
    this.JSONView = null;
    this.state = {
        view: '1'
    }
}
constructor(props) {
    super(props);
    this.input = null;
    this.state = {
        fetching: false,
        showDot: false,
        uploading: false,
        uploadStatus: 'active',
        pwd: [],
        objects: [],
        percent: 0
    }
}
componentDidMount

共 29 處。都是用于發(fā)送 ajax 請求數(shù)據(jù)。摘錄如下:

store.fetchRecords();
http.post('/api/config/history/'
this.updateValue()

Tip: 感覺請求數(shù)據(jù)時機有點遲。因為 vue 中請求數(shù)據(jù)我們通常會寫在 created 生命周期中,能更快獲取到服務端數(shù)據(jù),減少頁面loading 時間。

0 處使用

以下生命周期方法都在 spug 中使用。

  • shouldComponentUpdate
  • componentDidUpdate
  • componentWillUnmount
  • componentDidCatch(構造函數(shù)、渲染、生命周期中未捕獲的錯誤)
  • 過時的生命周期:componentWillMount、componentWillReceiveProps、componentWillUpdate。
  • 不建議使用:forceUpdate、shouldComponentUpdate
  • 新增生命周期方法:getDerivedStateFromProps、getSnapshotBeforeUpdate
React.useEffect

我們知道 React.useEffect 可以在函數(shù)組件中模擬 componentDidMount、componentDidUpdate、componentWillUnmount。就像這樣:

// 相當于 componentDidMount()
React.useEffect(() => {
    console.log('a')
}, [])
// 相當于 componentDidMount()、componentDidUpdate()
React.useEffect(() => {
    console.log('a')
})
// 相當于 componentDidMount、componentWillUnmount
React.useEffect(() => {
    console.log('a')
    return () => {
        console.log('b')
    }
}, [])

Tip: 有關 React.useEffect 的詳細介紹請看 這里 -> 體驗 useEffect

搜索 useEffect,發(fā)現(xiàn) spug 有 126 處。作用就這 3 點:

  • 模擬 componentDidMount 發(fā)送網(wǎng)絡請求,或初始化數(shù)據(jù),或設置定時器
  • 模擬 componentWillUnmount 刪除定時器
  • 模擬 componentDidUpdate

摘錄如下:

// 模擬 componentDidMount,用于發(fā)送網(wǎng)絡請求
useEffect(() => {
    setFetching(true);
    http.get('/api/app/deploy/')
        .then(res => setDeploys(res))
        .finally(() => setFetching(false))
    if (!envStore.records.length) {
        envStore.fetchRecords().then(_initEnv)
    } else {
        _initEnv()
    }
}, [])
// 模擬 componentDidMount 發(fā)送網(wǎng)絡請求、添加定時器,模擬 componentWillUnmount 刪除定時器
useEffect(() => {
    fetch();
    listen();
    timer = setInterval(() => {
      if (ws.readyState === 1) {
        ws.send('ping')
      } else if (ws.readyState === 3) {
        listen()
      }
    }, 10000)
    return () => {
      if (timer) clearInterval(timer);
      if (ws.close) ws.close()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
React.useEffect(() => {
    store.fetchGitlabList()
    store.fetchProjectName()
}, [])
// 模擬 componentDidMount()、componentDidUpdate()
useEffect(() => {
    setLoading(true);
    http.get('/api/home/alarm/', {params})
      .then(res => setRes(res))
      .finally(() => setLoading(false))
}, [params])
useEffect(() => {
    let socket;
    initialTerm()
    http.get(`/api/repository/${store.record.id}/`)
      .then(res => {
        term.write(res.data)
        setStep(res.step)
        if (res.status === '1') {
          socket = _makeSocket(res.index)
        } else {
          setStatus('wait')
        }
      })
      .finally(() => setFetching(false))
    return () => socket && socket.close()
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, [])
// 寫多個 useEffect 也可以
useEffect(() => {
    if (!loading) {
      http.get('/api/exec/history/')
        .then(res => setHistories(res))
    }
}, [loading])

useEffect(() => {
    return () => {
        store.host_ids = []
        if (store.showConsole) {
        store.switchConsole()
        }
    }
}, [])
// 掛載時注冊事件 resize,卸載時注銷事件 resize
useEffect(() => {
    store.tag = ''
    gCurrent = current
    const fitPlugin = new FitAddon()
    term.setOption('disableStdin', false)
    term.setOption('fontFamily', 'Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei')
    term.setOption('theme', {background: '#f0f0f0', foreground: '#000', selection: '#999', cursor: '#f0f0f0'})
    term.loadAddon(fitPlugin)
    term.open(el.current)
    fitPlugin.fit()
    term.write('\x1b[36m### WebSocket connecting ...\x1b[0m')
    const resize = () => fitPlugin.fit();
    window.addEventListener('resize', resize)
    setTerm(term)

    return () => window.removeEventListener('resize', resize);
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

父子組件的生命周期

筆者創(chuàng)建父子兩個組件,加入常見的生命周期方法(如:componentDidMount、shouldComponentUpdate、componentDidUpdate、componentWillUnmount、componentDidCatch),功能很簡單,子組件的文案來自父組件的 state,父組件的 state.text 能被用戶通過 input 修改。

代碼如下:

<script type="text/babel">
    class ChildComponent extends React.Component {
        static propTypes = {
            name: PropTypes.string
        };
        static defaultProps = (function () {
            console.log("ChildComponent : defaultProps。孩子的 defaultProps 是");
            return {};
        })();
        constructor(props) {
            super(props);
            console.log("ChildComponent: state。孩子的 state 是");
            this.state = {
                text: "peng"
            };
        }

        // 組件掛載后(插入 DOM 樹中)立即調(diào)用。常做網(wǎng)絡請求
        componentDidMount() {
            console.log("ChildComponent : componentDidMount");
        }

        shouldComponentUpdate(nextProps, nextState) {
            console.log("<ChildComponent/> - shouldComponentUpdate()");
            return true;
        }
        // React 更新 dom 和 refs 后調(diào)用
        componentDidUpdate(previousProps, previousState) {
            console.log("ChildComponent: componentDidUpdate");
        }
        componentWillUnmount() {
            console.log("ChildComponent: componentWillUnmount");
        }

        render() {
            if (this.props.content === 'jiali12') {
                throw new Error("模擬組件報錯");
            }

            console.log("ChildComponent: render");
            // 下面兩種寫法等效。一個是單根一個是多根。

            // return <div className="subClass">
            //     sub
            //     <p>{this.props.content}</p>
            // </div>

            // 下面這種數(shù)組的寫法需要給每個元素添加 key,否則報錯如下:
            // Warning: Each child in a list should have a unique "key" prop.
            // React 要求每個被迭代項傳遞一個 key,對于 render() 方法返回的任何數(shù)組組件亦如此。
            return [
                <div key="name" className="subClass">sub</div>,
                <p key="content">{this.props.content}</p>
            ]

        }
    }

    class ParentComponent extends React.Component {
        static defaultProps = (function () {
            console.log("ParentComponent: defaultProps。我的 defaultProps 是");
            return {
                true: false
            };
        })();
        constructor(props) {
            super(props);
            console.log("ParentComponent: state。我的 state 是");
            this.state = { text: "jiali" };
            this.onInputChange = this.onInputChange.bind(this);
        }

        onInputChange(e) {
            const text = e.target.value;
            this.setState(() => ({ text: text }));
        }
        componentDidMount() {
            console.log("ParentComponent: componentDidMount");
        }
        componentDidUpdate(previousProps, previousState) {
            console.log("ParentComponent: componentDidUpdate");
        }
        componentWillUnmount() {
            console.log("ParentComponent: componentWillUnmount");
        }
        // 此生命周期在后代組件拋出錯誤后被調(diào)用
        componentDidCatch(err, errorInfo) {
            console.log("componentDidCatch");
            this.setState(() => ({ err, errorInfo }));
        }

        render() {
            if (this.state.err) {
                return <div>降級處理</div>
            }
            console.log("ParentComponent: render");
            return <div className="parentClass">

                <p>parent</p>
                <input
                    key="input"
                    value={this.state.text}
                    onChange={this.onInputChange}
                />
                <ChildComponent content={this.state.text} />
            </div>

        }
    }

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

瀏覽器中生成的頁面結(jié)構如下:

<div id="root">
    <div class="parentClass">
        <p>parent</p>
        <input value="jiali">
        <!-- 子組件 -->
        <div class="subClass">sub</div>
        <p>jiali</p>
        <!-- /子組件 -->
    </div>
</div>

控制臺輸出:

ChildComponent : defaultProps。孩子的 defaultProps 是
ParentComponent: defaultProps。我的 defaultProps 是
ParentComponent: state。我的 state 是
ParentComponent: render
ChildComponent: state。孩子的 state 是
ChildComponent: render
ChildComponent : componentDidMount
ParentComponent: componentDidMount

Tip:盡管初始 state 和屬性并不使用特定的生命周期方法,但他們?yōu)榻M件提供數(shù)據(jù)方面發(fā)揮了重要作用,所以有必要把它們作為生命周期的一部分

此刻是初次渲染,也就是掛載時,根據(jù)輸出日志我們知道:

  • 先 defaultProps 后 state
  • 先 render 后 componentDidMount
  • 先 render 父組件,后 render 子組件
  • 先掛載子組件,后掛載父組件

為何先 render 父組件,又先掛載子組件?

Tip: 其實 vue 也是這樣,請看 這里

筆者推測如下:

入口是這里:

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

先渲染父組件,發(fā)現(xiàn)里面有子組件,接著渲染子組件。

我們將虛擬 Dom看做真實 Dom,虛擬 Dom 如何生成真實 Dom 的我們暫時不去管它,這是 React 做的事情。虛擬 DOM 樹的生成過程前文我們已經(jīng)說了:像一個小孩不停的問“里面是什么?”,”里面是什么?“,直到搞懂所有的子孫組件,就可以形成一顆完整的樹。

接著往 input 中輸入 1(此刻顯示jiali1),控制臺輸出:

ParentComponent: render
<ChildComponent/> - shouldComponentUpdate()
ChildComponent: render
ChildComponent: componentDidUpdate
ParentComponent: componentDidUpdate

同樣,先執(zhí)行父組件的 render 再執(zhí)行子組件的 render,先調(diào)用子組件的 componentDidUpdate 再調(diào)用父組件的 componentDidUpdate??梢苑g成:

  • 要渲染父組件
  • 里面有子組件需要渲染
  • 子組件更新完畢
  • 父組件更新完畢

接著往 input 中輸入 2(此刻顯示jiali12),觸發(fā)子組件 render 里面報錯,父組件中的 componentDidCatch 被執(zhí)行,實現(xiàn)降級處理,頁面顯示 降級處理。

到此這篇關于React 的數(shù)據(jù)流和生命周期的文章就介紹到這了,更多相關React 生命周期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • React+TS+IntersectionObserver實現(xiàn)視頻懶加載和自動播放功能

    React+TS+IntersectionObserver實現(xiàn)視頻懶加載和自動播放功能

    通過本文的介紹,我們學習了如何使用 React + TypeScript 和 IntersectionObserver API 來實現(xiàn)一個視頻播放控制組件,該組件具有懶加載功能,只有在用戶滾動頁面且視頻進入視口時才開始下載視頻資源,需要的朋友可以參考下
    2023-04-04
  • React-View-UI組件庫封裝Loading加載中源碼

    React-View-UI組件庫封裝Loading加載中源碼

    這篇文章主要介紹了React-View-UI組件庫封裝Loading加載樣式,主要包括組件介紹,組件源碼及組件測試源碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • React在弱網(wǎng)環(huán)境下限制按鈕多次點擊,防止重復提交問題

    React在弱網(wǎng)環(huán)境下限制按鈕多次點擊,防止重復提交問題

    這篇文章主要介紹了React在弱網(wǎng)環(huán)境下限制按鈕多次點擊,防止重復提交問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 用React實現(xiàn)一個類 chatGPT 的交互式問答組件的方法詳解

    用React實現(xiàn)一個類 chatGPT 的交互式問答組件的方法詳解

    這篇文章主要給大家詳細介紹如何用React實現(xiàn)一個類 chatGPT 的交互式問答組件的方法,文中有詳細的代碼示例,對我們學習有一定的幫助,需要的朋友可以參考下
    2023-06-06
  • React實現(xiàn)單向數(shù)據(jù)流的方法

    React實現(xiàn)單向數(shù)據(jù)流的方法

    本文主要介紹了React實現(xiàn)單向數(shù)據(jù)流的方法
    2023-04-04
  • React?Native?Modal?的封裝與使用實例詳解

    React?Native?Modal?的封裝與使用實例詳解

    這篇文章主要介紹了React?Native?Modal?的封裝與使用,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • React高階組件使用教程詳解

    React高階組件使用教程詳解

    高階組件就是接受一個組件作為參數(shù)并返回一個新組件(功能增強的組件)的函數(shù)。這里需要注意高階組件是一個函數(shù),并不是組件,這一點一定要注意,本文給大家分享React 高階組件HOC使用小結(jié),一起看看吧
    2022-12-12
  • React項目使用ES6解決方案及JSX使用示例詳解

    React項目使用ES6解決方案及JSX使用示例詳解

    這篇文章主要為大家介紹了React項目使用ES6解決方案及JSX使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • React實現(xiàn)Excel文件的導出與在線預覽功能

    React實現(xiàn)Excel文件的導出與在線預覽功能

    這篇文章主要為大家詳細介紹了如何利用?React?18?的強大功能,演示如何使用?React?18?編寫?Excel?文件的導出與在線預覽功能,需要的小伙伴可以參考下
    2023-12-12
  • 用React Native制作一個簡單的游戲引擎

    用React Native制作一個簡單的游戲引擎

    今天給大家分享的是使用React Native制作一個簡單的游戲,這個游戲可以跨平臺操作,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,對React Native游戲相關知識感興趣的朋友一起看看吧
    2021-05-05

最新評論