1. 程式人生 > >30天入坑React ---------------day10 Interactivity

30天入坑React ---------------day10 Interactivity

這篇文章是系列的一部分 。

在本系列中,我們將從非常基礎開始,逐步瞭解您需要了解的所有內容,以便開始使用React。如果您曾經想學習React,那麼這裡就是您的最佳選擇!

30天的React Mini-Ebook

互動

今天,我們將介紹如何為我們的應用程式新增互動性,使其具有吸引力和動態性。

通過這一點,我們構建了少數幾個元件,而沒有增加太多的使用者互動。今天,我們要改變這一點。

使用者互動

瀏覽器是一個事件驅動的應用程式。使用者在瀏覽器中執行的所有操作都會觸發事件,從單擊按鈕到甚至只是移動滑鼠。在純的JavaScript中,我們可以監聽這些事件並附加的JavaScript函式來與它們進行互動。

例如,可以我們mousemove

使用JS 將函式附加到瀏覽器事件:

export const go = () => {
  const ele = document.getElementById('mousemove');
  ele.innerHTML = 'Move your mouse to see the demo';
  ele.addEventListener('mousemove', function(evt) {
    const { screenX, screenY } = evt;
    ele.innerHTML = '<div>Mouse is at: X: ' +
          screenX + ', Y: ' + screenY +
                    '</div>';
  })
}

這導致以下功能:

移動滑鼠以檢視演示

在陣營中,我們不必在原始的JavaScript中與瀏覽器的事件迴圈互動,因為陣營為我們提供了一種處理事件的方法props

例如,mousemove要從React上面的(相當不起眼的)演示中監聽事件,我們將設定prop onMouseMove(注意事件名稱的camelcasing)。

<div onMouseMove={(evt) => console.log(evt)}>
  Move the mouse over this text
</div>

反應了提供很多最props我們可以設定來監聽不同的瀏覽器事件,例如點選,觸控,拖動,滾動,選擇事件等等(參閱請

事件文件以電子雜誌所有這些事件的列表)。

要檢視其中的一些實際操作,的英文以下props我們可以傳遞給我們的元素的一些小_ECSHOP演示。列表中的每個文字元素都會設定它列出的道具。嘗試使用列表並檢視在元素中如何呼叫和處理事件(所有事件都在文字上設定,而不是列表項):

將我們在onClick整個應用程式中使用支柱相當多,所以熟悉它是個好主意。在我們的活動列表標題中,我們有一個搜尋圖示,我們還沒有連線到顯示搜尋框。

我們想要的互動的英文<input />在使用者點選搜尋圖示種植時顯示搜尋。回想一下,的我們Header元件的英文這樣實現的:

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }

  // toggle visibility when run on the state
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }

  render() {
    // Classes to add to the <input /> element
    let searchInputClasses = ["searchInput"];

    // Update the class array if the state is visible
    if (this.state.searchVisible) {
      searchInputClasses.push("active");
    }

    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className={searchInputClasses.join(' ')}
          placeholder="Search ..." />

        {/* Adding an onClick handler to call the showSearch button */}
        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

使用者當單擊該<div className="fa fa-search searchIcon"></div>元素時,我們將要執行一個函式來更新元件的狀態,以便searchInputClasses更新物件。使用onClick處理程式,這非常簡單。

讓我們使這個元件有狀態(它需要跟蹤搜尋欄位是否應該顯示)。我們可以使用constructor()函式將我們的元件轉換為有狀態:

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }
  // ...
}

的英文什麼constructor功能?

在JavaScript的中,該constructor函式的英文在建立³³物件時執行的函式。它返回對建立例項的物件函式的引用prototype

簡單來說,建構函式是在JavaScript的執行時建立新物件時執行的函式。我們將使用建構函式方法在物件上設定例項變數,因為它在建立物件時正確執行。

使用ES6類語法建立³³物件時,必須我們super()在任何其他方法之前呼叫該方法。呼叫該super()函式呼叫父類的constructor()函式。我們將使用相同的引數呼叫它,我們因為呼叫constructor()了類的函式。

當用戶單擊該按鈕時,將我們要更新狀態以表示該searchVisible標誌已更新。由於我們希望使用者能夠在<input />第二次單擊搜尋圖示種植後關閉/隱藏欄位,因此將我們e月刊狀態而不是將其設定為真。

讓我們建立這個方法來繫結我們的單擊事件:

class Header extends React.Component {
  // ...
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }
  // ...
}

最後,我們可以onClick在icon元素上附加一個單擊處理程式(使用prop)來呼叫我們的新showSearch()方法。我們Header元件的整個更新原始碼如下所示:

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }

  // toggle visibility when run on the state
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }

  render() {
    // Classes to add to the <input /> element
    let searchInputClasses = ["searchInput"];

    // Update the class array if the state is visible
    if (this.state.searchVisible) {
      searchInputClasses.push("active");
    }

    return (
      <div className="header">
        <div className="fa fa-more"></div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className={searchInputClasses.join(' ')}
          placeholder="Search ..." />

        {/* Adding an onClick handler to call the showSearch button */}
        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

嘗試單擊搜尋圖示並觀察輸入欄位的顯示和消失(動畫效果由CSS動畫處理)。

輸入事件

每當我們在React中構建表單時,我們將使用React提供的輸入事件。最值得注意的是,我們最常使用onSubmit()onChange()道具。

讓我們更新我們的搜尋框演示,以便在更新時捕獲搜尋欄位內的文字。每當一個<input />欄位具有onChange()prop設定時,它將在每次欄位改變時呼叫該函式。當我們點選它並開始輸入時,將呼叫該函式。

使用此prop,我們可以捕獲我們州的欄位值。

我們不是更新我們的<Header />元件,而是建立一個包含<form />元素的新子元件。通過將表單處理職責移動到它自己的表單,我們可以簡化<Header />程式碼,當我們的使用者提交表單時,我們可以呼叫標題的父級(這是通常的React模式)。

讓我們建立一個我們稱之為的新元件SearchForm。這個新元件是一個有狀態元件,因為我們需要保持搜尋輸入的值(跟蹤它的變化):

class SearchForm extends React.Component {
  // ...
  constructor(props) {
    super(props);

    this.state = {
      searchText: ''
    }
  }
  // ...
}

現在,我們已經在<Header />元件中編寫了表單的HTML ,所以讓我們從Header元件中獲取它並從我們的SearchForm.render()函式返回它:

class SearchForm extends React.Component {
  // ...
  render() {
    const { searchVisible } = this.state;
    let searchClasses = ['searchInput']
    if (searchVisible) {
      searchClasses.push('active')
    }

    return (
      <form className='header'>
        <input
          type="search"
          className={searchClasses.join(' ')}
          onChange={this.updateSearchInput.bind(this)}
          placeholder="Search ..." />

        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </form>
    );
  }
}

請注意,我們在我們的<input />領域丟失了樣式。由於我們searchVisible的新元件中不再具有狀態,因此我們不能再使用它來設定樣式<input />但是,我們可以從我們的Header元件傳遞一個prop,它告訴SearchForm將輸入呈現為可見。

讓我們定義searchVisibleprop(PropTypes當然使用)並更新render函式以使用新的prop值來顯示(或隱藏)搜尋<input />。我們還將欄位可見性的預設值設定為false(因為我們的Header顯示/隱藏它很好):

class SearchForm extends React.Component {
  static propTypes = {
    onSubmit: PropTypes.func.isRequired,
    searchVisible: PropTypes.bool
  }
  // ...
}

現在我們將樣式返回到<input />元素上,讓我們新增使用者在搜尋框中鍵入的功能,我們將要捕獲搜尋欄位的值。我們可以通過將onChangeprop新增到<input />元素並向其傳遞函式來實現此工作流程,以便每次<input />更改元素時呼叫它。

class SearchForm extends React.Component {
  // ...
  updateSearchInput(e) {
    const val = e.target.value;
    this.setState({
      searchText: val
    });
  }
  // ...
  render() {
    const { searchVisible } = this.state;
    let searchClasses = ['searchInput']
    if (searchVisible) {
      searchClasses.push('active')
    }

    return (
      <form className='header'>
        <input
          type="search"
          className={searchClasses.join(' ')}
          onChange={this.updateSearchInput.bind(this)}
          placeholder="Search ..." />

        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </form>
    );
  }
}

當我們輸入欄位時,updateSearchInput()將呼叫該函式。我們將通過更新狀態來跟蹤表單的價值。在updateSearchInput()函式中,我們可以直接呼叫this.setState()以更新元件的狀態。

該值儲存在event物件的目標上event.target.value

class SearchForm extends React.Component {
  // ...
  updateSearchInput(e) {
    const val = e.target.value;
    this.setState({
      searchText: val
    });
  }
  // ...
}

受控制與不受控制

我們正在建立所謂的不受控制的元件,因為我們沒有設定<input />元素的值。我們現在無法對輸入文字值進行任何驗證或後處理。

如果我們想要驗證欄位或操縱<input />元件的值,我們必須建立所謂的受控元件,這實際上只意味著我們使用valueprop 傳遞一個值。受控元件版本的render()功能如下所示:

class SearchForm extends React.Component {
  render() {
    return (
      <input
        type="search"
        value={this.state.searchText}
        className={searchInputClasses}
        onChange={this.updateSearchInput.bind(this)}
        placeholder="Search ..." />
    );
  }
}

截至目前,我們無法實際提交表單,因此我們的使用者無法真正搜尋。讓我們改變這一點。我們要將<input />元件包裝在<form />DOM元素中,以便我們的使用者可以按回車鍵提交表單。我們可以通過使用元素onSubmit上的prop 來捕獲表單提交<form />

讓我們更新render()函式以反映這一變化。

class SearchForm extends React.Component {
  // ...
  submitForm(e) {
    e.preventDefault();

    const {searchText} = this.state;
    this.props.onSubmit(searchText);
  }
  // ...
  render() {
    const { searchVisible } = this.props;
    let searchClasses = ['searchInput']
    if (searchVisible) {
      searchClasses.push('active')
    }

    return (
      <form onSubmit={this.submitForm.bind(this)}>
        <input
          type="search"
          className={searchClasses.join(' ')}
          onChange={this.updateSearchInput.bind(this)}
          placeholder="Search ..." />
      </form>
    );
  }
}

我們馬上打電話event.preventDefault()給這個submitForm()功能。這會阻止瀏覽器冒泡事件,這會導致整個頁面的預設行為重新載入(瀏覽器提交表單時的預設功能)。

現在,當我們輸入<input />欄位並按Enter鍵時,將submitForm()使用事件物件呼叫該函式。

所以...很好,我們可以提交表格和內容,但我們什麼時候才能進行搜尋?出於演示的目的,現在,我們會通過搜尋文字了親子部分構成的鏈,因此Header可以決定什麼搜尋。

SearchForm元件當然不知道它在搜尋什麼,所以我們必須將責任傳遞給鏈。我們將使用這個回撥策略。

為了將搜尋功能傳遞給鏈,我們SearchForm需要接受一個支柱函式來在提交表單時呼叫。讓我們定義一個我們稱之為onSubmit可以傳遞給我們SearchForm元件的道具。作為優秀的開發人員,還將我們為此功能新增預設prop值和a 。由於我們要確保定義,我們將把道具設定為必需的道具:propTypeonSubmitonSubmit()onSubmit

class SearchForm extends React.Component {
  static propTypes = {
    onSubmit: PropTypes.func.isRequired,
    searchVisible: PropTypes.bool
  }
  // ...
  static defaultProps = {
    onSubmit: () => {},
    searchVisible: false
  }
  // ...
}

提交表單時,我們可以直接從中呼叫此函式props。由於我們在狀態中跟蹤搜尋文字,可以我們使用searchText狀態中的值呼叫函式,因此onSubmit()函式只獲取值而不需要處理事件。

class SearchForm extends React.Component {
  // ...
  submitForm(event) {
    // prevent the form from reloading the entire page
    event.preventDefault();
    // call the callback with the search value
    this.props.onSubmit(this.state.searchText);
  }
}

現在,當用戶按下回車鍵時,可以我們通過我們的元件呼叫此onSubmit()函式。propsHeader

可以我們SearchForm在元件中使用這個元件,Header並將它們傳遞給我們定義的兩個道具(searchVisibleonSubmit):

import React from 'react';
import SearchForm from './SearchFormWithSubmit'

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }

  // toggle visibility when run on the state
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }

  render() {
    // Classes to add to the <input /> element
    let searchInputClasses = ["searchInput"];

    // Update the class array if the state is visible
    if (this.state.searchVisible) {
      searchInputClasses.push("active");
    }

    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <SearchForm
          searchVisible={this.state.searchVisible}
          onSubmit={this.props.onSubmit} />

        {/* Adding an onClick handler to call the showSearch button */}
        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

export default Header

現在我們有一個搜尋表單元件,可以我們在我們的應用程式中使用狀語從句:重用當然,我們實際上還沒有搜尋任何東西。讓我們解決這個問題並實施搜尋。

要在我們的元件中實現搜尋,需要我們搜尋將職責從我們的Header元件再次傳遞給我們將呼叫的容器元件Panel

首先,我們讓實現從子Panel容器中的容器到Header元件的回撥到父元件的相同模式。

Header元件上,我們讓更新propTypes一個我們將定義為支柱的道具onSearch

class Header extends React.Component {
  // ...
}
Header.propTypes = {
  onSearch: PropTypes.func
}

Header元件的'submitForm()'函式內部,呼叫此onSearch()丙我們將傳遞給它:

class Header extends React.Component {
  // ...
  submitForm(val) {
    this.props.onSearch(val);
  }
  // ...
}
Header.propTypes = {
  onSearch: PropTypes.func
}

請注意,我們的虛擬樹看起來像這樣:

<Panel>
  <Header>
    <SearchForm></SearchForm>
  </Header>
</Panel

<SearchForm />更新時,它會傳遞它對搜尋輸入對其父級的更改的感知<Header />,它將當向上網站傳遞給<Panel />元件時,此方法在作出反應程式應用中非常常見,併為我們的元件提供了一組良好的功能隔離。

回到Panel我們在第7構建天的元件中,將我們函式傳遞給Header作為onSearch()支柱的函式Header。我們在這裡說的是,當提交搜尋表單時,我們希望搜尋表單回撥到標題元件,呼叫然後Panel元件來處理搜尋。

由於Header元件不控制內容列表,Panel元件確實如此,我們必須將責任再提高一級,正如我們在此定義的那樣。

無論如何,的我們Panel元件基本上的英文Content我們之前處理過的元件的副本:

class Panel extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false, // <~ set loading to false
      activities: data,
      filtered: data,
    }
  }

  componentDidMount() {this.updateData();}
  componentWillReceiveProps(nextProps) {
    // Check to see if the requestRefresh prop has changed
    if (nextProps.requestRefresh !== this.props.requestRefresh) {
      this.setState({loading: true}, this.updateData);
    }
  }

  handleSearch = txt => {
    if (txt === '') {
      this.setState({
        filtered: this.state.activities
      })
    } else {
      const { activities } = this.state
      const filtered = activities.filter(a => a.actor && a.actor.login.match(txt))
      this.setState({
        filtered
      })
    }
  }

  // Call out to github and refresh directory
  updateData() {
    this.setState({
      loading: false,
      activities: data
    }, this.props.onComponentRefresh);
  }

  render() {
    const {loading, filtered} = this.state;

    return (
      <div>
        <Header
          onSubmit={this.handleSearch}
          title="Github activity" />
        <div className="content">
          <div className="line"></div>
          {/* Show loading message if loading */}
          {loading && <div>Loading</div>}
          {/* Timeline item */}
          {filtered.map((activity) => (
            <ActivityItem
              key={activity.id}
              activity={activity} />
          ))}

        </div>
      </div>
    )
  }
}

我們讓我們更新的狀態以所有遊戲一個searchFilter字串,它只是搜尋值:

class Panel extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      searchFilter: '',
      activities: []
    }
  }
}

為了實際處理搜尋,我們需要將一個onSearch()函式傳遞給我們的Header元件。讓我們onSearch()Panel元件中定義一個函式,並將其傳遞給函式中的Header道具render()

class Panel extends React.Component {
  // ...
  // after the content has refreshed, we want to
  // reset the loading variable
  onComponentRefresh() {this.setState({loading: false});}

  handleSearch(val) {
    // handle search here
  }

  render() {
    const {loading} = this.state;

    return (
      <div>
        <Header
          onSearch={this.handleSearch.bind(this)}
          title="Github activity" />
        <Content
          requestRefresh={loading}
          onComponentRefresh={this.onComponentRefresh.bind(this)}
          fetchData={this.updateData.bind(this)} />
      </div>
    )
  }
}

在我們這裡所做的就是新增一個handleSearch()函式並將其傳遞給標題。現在,當用戶在搜尋框中鍵入時,將handleSearch()呼叫我們Panel元件上的函式。

實際要實現搜尋,需要我們跟蹤此字串並更新我們的updateData()功能以考慮搜尋過濾。首先,我們讓設定searchFilter狀態。我們還可以Content通過設定loading為真來強制重新載入資料,因此我們可以一步完成:

class Panel extends React.Component {
  // ...
  handleSearch(val) {
    this.setState({
      searchFilter: val,
      loading: true
    });
  }
  // ...
}

最後,我們讓我們更新的updateData()功能以考慮搜尋

class SearchableContent extends React.Component {
  // ...
      this.setState({loading: true}, this.updateData);
  // ...
}

雖然這可能看起來很複雜,它但實際上幾乎與我們現有的updateData()函式完全相同,除了我們更新fetch()查詢查詢結果以呼叫filter()JSON集合上的方法。

所有collection.filter()函式都執行為每個元素傳入的函式,並過濾報道檢視虛假值的值,保留返回真值的值。我們的搜尋功能只是在Github的活動actor.login(Github的使用者)上查詢匹配,以檢視它是否與該searchFilter值匹配。

隨著updateData()功能的更新,我們的搜尋已經完成。

嘗試搜尋auser

現在我們有一個3層應用程式元件,用於處理巢狀子元件的搜尋。我們用這篇文章從初學者跳到中級。拍拍自己的背部。這是一些沉重的材料。請確保您理解這一點,因為我們會經常使用我們今天介紹的這些概念。

在下一節中,我們將跳出來看看構建元件。

學習REACT正確的方法

React和朋友的最新,深入,完整的指南。

下載第一章

❮上一個

下一章:

純元件