React入門(mén)教程(9)Ajax與React的上下文

ajax請(qǐng)求

react的組件中列荔,一般我們?cè)?componentDidMount事件中做ajax請(qǐng)求五辽,并獲得數(shù)據(jù)后修改state场斑。

請(qǐng)求后臺(tái)獲取數(shù)據(jù)可以用任何的ajax庫(kù),建議使用: 原生的fetch或者axios庫(kù)脏毯。

例如新聞案例:

import React, { Component } from 'react'
import axios from 'axios';

class NewsList extends Component {
  constructor(opt) {
    super(opt);
    this.state = {
      newsList: []
    };
  }

  componentDidMount() {
    // 發(fā)送ajax請(qǐng)求到后臺(tái)并獲取數(shù)據(jù)
    axios
      .get('/db.json')
      .then(res => {
        // console.log(res.data.news);
        this.setState({newsList: res.data.news});
      });
  }

  delNews(id) {
    // 不模擬從后臺(tái)ajax請(qǐng)求刪除數(shù)據(jù)
    // 直接在當(dāng)前數(shù)組中移除數(shù)據(jù)。
    if(window.confirm('您是否要真的刪除嗎幔崖?')) {
      this.setState(preState => {
        return {
          newsList: preState.newsList.filter( item => item.id !== id)
        }
      });
    }
  }

  render () {
    return (
      <div>
        <table className="table is-striped is-hoverable is-bordered is-fullwidth">
          <thead>
            <tr>
              <th>編號(hào)</th>
              <th>新聞標(biāo)題</th>
              <th>編輯</th>
            </tr>
          </thead>
          <tbody>
          {
            this.state.newsList.map((item, index) => {
              return (
                <tr key={index}>
                  <td>{item.id}</td>
                  <td>{item.title}</td>
                  <td>
                    <button
                      className="button is-primary" 
                      onClick={ this.delNews.bind(this, item.id) }
                    >
                      刪除
                    </button>
                  </td>
                </tr>
              )
            })
          }
          </tbody>
        </table>
      </div>
    )
  }
}

export default NewsList;

React和DOM之間的屬性區(qū)別

React實(shí)現(xiàn)了一套與瀏覽器無(wú)關(guān)的DOM系統(tǒng)食店,兼顧了性能和跨瀏覽器的兼容性。在React和Html之間有許多屬性的行為是不同的赏寇。

checked

checked屬性受類型為checkboxradio<input>組件的支持吉嫩。你可以用它來(lái)設(shè)定是否組件是被選中的。這對(duì)于構(gòu)建受控組件很有用嗅定。與之相對(duì)defaultChecked這是非受控組件的屬性自娩,用來(lái)設(shè)定對(duì)應(yīng)組件首次裝載時(shí)是否選中狀態(tài)。

className

使用className屬性指定一個(gè)CSS類渠退。這個(gè)特性適用于所有的常規(guī)DOM節(jié)點(diǎn)和SVG元素忙迁,比如<div><a>和其它的元素碎乃。

如果你在React中使用Web組件(這是一種不常見(jiàn)的使用方式)姊扔,請(qǐng)使用class屬性來(lái)代替。

dangerouslySetInnerHTML

dangerouslySetInnerHTML是React提供的替換瀏覽器DOM中的innerHTML接口的一個(gè)函數(shù)梅誓。一般而言恰梢,使用JS代碼設(shè)置HTML文檔的內(nèi)容是危險(xiǎn)的,因?yàn)檫@樣很容易把你的用戶信息暴露給跨站腳本攻擊.所以梗掰,你雖然可以直接在React中設(shè)置html的內(nèi)容删豺,但你要使用 dangerouslySetInnerHTML 并向該函數(shù)傳遞一個(gè)含有__html鍵的對(duì)象,用來(lái)提醒你自己這樣做很危險(xiǎn)愧怜。例如:

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

htmlFor

因?yàn)?code>for是在javascript中的一個(gè)保留字呀页,React元素使用 htmlFor代替。

onChange

onChange事件的行為正如你所期望的:無(wú)論一個(gè)表單字段何時(shí)發(fā)生變化拥坛,這個(gè)事件都會(huì)被觸發(fā)蓬蝶。我們故意不使用瀏覽器已有的默認(rèn)行為,因?yàn)?code>onChange在瀏覽器中的行為和名字不相稱猜惋,React依靠這個(gè)事件實(shí)時(shí)處理用戶輸入丸氛。

selected

selected屬性被<option>組件支持。你可以使用該屬性設(shè)定組件是否被選擇著摔。這對(duì)構(gòu)建受控組件很有用缓窜。

style

style屬性接受一個(gè)JavaScript對(duì)象,其屬性用小駝峰命名法命名,而不是接受CSS字符串禾锤。這和DOM中styleJavaScript 屬性是一致性的私股,是更高效的,而且能夠防止XSS的安全漏洞恩掷。例如:

const divStyle = {
  color: 'blue',
  backgroundImage: 'url(' + imgUrl + ')',
};

function HelloWorldComponent() {
  return <div style={divStyle}>Hello World!</div>;
}

注意樣式不會(huì)自動(dòng)補(bǔ)齊前綴倡鲸。為了支持舊的瀏覽器,你需要手動(dòng)提供相關(guān)的樣式屬性:

const divStyle = {
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

function ComponentWithTransition() {
  return <div style={divStyle}>This should work cross-browser</div>;
}

樣式key使用小駝峰命名法是為了從JS中訪問(wèn)DOM節(jié)點(diǎn)的屬性保持一致性(例如 node.style.backgroundImage)黄娘。供應(yīng)商前綴除了ms峭状,都應(yīng)該以大寫(xiě)字母開(kāi)頭。這就是為什么WebkitTransition有一個(gè)大寫(xiě)字母W逼争。

React將自動(dòng)添加一個(gè)"px"后綴到某些數(shù)字內(nèi)聯(lián)樣式屬性优床。如果你希望使用不同于"px"的其他單位,指定值為帶渴望單位的字符串誓焦。例如:

// Result style: '10px'
<div style={{ height: 10 }}>
  Hello World!
</div>

// Result style: '10%'
<div style={{ height: '10%' }}>
  Hello World!
</div>

不是所有樣式屬性被轉(zhuǎn)化為像素字符串胆敞,盡管如此。某些個(gè)保持無(wú)單位(例如 zoom, order, flex)罩阵。A complete list of 無(wú)單位屬性 can be seen here.

value

value屬性受到<input><textarea> 組件的支持竿秆。你可以使用它設(shè)置組件的值。這對(duì)構(gòu)建受控組件非常有用稿壁。defaultValue屬性對(duì)應(yīng)的是非受控組件的屬性幽钢,用來(lái)設(shè)置組件第一次裝載時(shí)的值。

其他受支持的HTML屬性

As of React 16, 任何標(biāo)準(zhǔn)的或自定義的 DOM屬性都被充分支持傅是。

React 總是提供一個(gè)以 JavaScript為中心的API給DOM匪燕。因?yàn)镽eact組件對(duì)于自定義和DOM相關(guān)的屬性都經(jīng)常采用檐束。React使用小駝峰約定捐寥,正如DOM API:

<div tabIndex="-1" />      // Just like node.tabIndex DOM API
<div className="Button" /> // Just like node.className DOM API
<input readOnly={true} />  // Just like node.readOnly DOM API

這些屬性的工作類似于對(duì)應(yīng)的HTML屬性,除了上述文檔的特例囊榜。

(Context)上下文

Context 通過(guò)組件樹(shù)提供了一個(gè)傳遞數(shù)據(jù)的方法书闸,從而避免了在每一個(gè)層級(jí)手動(dòng)的傳遞 props 屬性尼变。

在一個(gè)典型的 React 應(yīng)用中,數(shù)據(jù)是通過(guò) props 屬性由上向下(由父及子)的進(jìn)行傳遞的浆劲,但這對(duì)于某些類型的屬性而言是極其繁瑣的(例如:地區(qū)偏好嫌术,UI主題),這是應(yīng)用程序中許多組件都所需要的牌借。 Context 提供了一種在組件之間共享此類值的方式度气,而不必通過(guò)組件樹(shù)的每個(gè)層級(jí)顯式地傳遞 props

簡(jiǎn)單說(shuō)就是膨报,當(dāng)你不想在組件樹(shù)中通過(guò)逐層傳遞props或者state的方式來(lái)傳遞數(shù)據(jù)時(shí)磷籍,可以使用Context來(lái)實(shí)現(xiàn)跨層級(jí)的組件數(shù)據(jù)傳遞适荣。

  • 使用props或者state傳遞數(shù)據(jù),數(shù)據(jù)自頂下流院领。
數(shù)據(jù)自頂下流
  • 使用Context弛矛,可以跨越組件進(jìn)行數(shù)據(jù)傳遞
跨越組件進(jìn)行數(shù)據(jù)傳遞

何時(shí)使用 Context

Context 設(shè)計(jì)目的是為共享那些被認(rèn)為對(duì)于一個(gè)組件樹(shù)而言是“全局”的數(shù)據(jù),例如當(dāng)前認(rèn)證的用戶栅盲、主題或首選語(yǔ)言汪诉。例如废恋,在下面的代碼中谈秫,我們通過(guò)一個(gè)“theme”屬性手動(dòng)調(diào)整一個(gè)按鈕組件的樣式:

function ThemedButton(props) {
  return <Button theme={props.theme} />;
}

// 中間組件
function Toolbar(props) {
  // Toolbar 組件必須添加一個(gè)額外的 theme 屬性
  // 然后傳遞它給 ThemedButton 組件
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

使用 context, 我可以避免通過(guò)中間元素傳遞 props:

// 創(chuàng)建一個(gè) theme Context,  默認(rèn) theme 的值為 light
const ThemeContext = React.createContext('light');

function ThemedButton(props) {
  // ThemedButton 組件從 context 接收 theme
  return (
    <ThemeContext.Consumer>
      {theme => <Button {...props} theme={theme} />}
    </ThemeContext.Consumer>
  );
}

// 中間組件
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

注意

不要僅僅為了避免在幾個(gè)層級(jí)下的組件傳遞 props 而使用 context,它是被用于在多個(gè)層級(jí)的多個(gè)組件需要訪問(wèn)相同數(shù)據(jù)的情景鱼鼓。

React.createContext

const {Provider, Consumer} = React.createContext(defaultValue);

創(chuàng)建一對(duì) { Provider, Consumer }拟烫。當(dāng) React 渲染 context 組件 Consumer 時(shí),它將從組件樹(shù)的上層中最接近的匹配的 Provider 讀取當(dāng)前的 context 值迄本。

如果上層的組件樹(shù)沒(méi)有一個(gè)匹配的 Provider硕淑,而此時(shí)你需要渲染一個(gè) Consumer 組件,那么你可以用到 defaultValue 嘉赎。這有助于在不封裝它們的情況下對(duì)組件進(jìn)行測(cè)試置媳。

Provider

<Provider value={/* some value */}>

React 組件允許 Consumers 訂閱 context 的改變。

接收一個(gè) value 屬性傳遞給 Provider 的后代 Consumers公条。一個(gè) Provider 可以聯(lián)系到多個(gè) Consumers拇囊。Providers 可以被嵌套以覆蓋組件樹(shù)內(nèi)更深層次的值。

Consumer

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

一個(gè)可以訂閱 context 變化的 React 組件靶橱。

接收一個(gè) 函數(shù)作為子節(jié)點(diǎn). 函數(shù)接收當(dāng)前 context 的值并返回一個(gè) React 節(jié)點(diǎn)寥袭。傳遞給函數(shù)的 value 將等于組件樹(shù)中上層 context 的最近的 Provider 的 value 屬性。如果 context 沒(méi)有 Provider 关霸,那么 value 參數(shù)將等于被傳遞給 createContext()defaultValue 传黄。

注意

關(guān)于此案例的更多信息, 請(qǐng)看 render props.

每當(dāng)Provider的值發(fā)生改變時(shí), 作為Provider后代的所有Consumers都會(huì)重新渲染。 從Provider到其后代的Consumers傳播不受shouldComponentUpdate方法的約束队寇,因此即使祖先組件退出更新時(shí)膘掰,后代Consumer也會(huì)被更新。

通過(guò)使用與Object.is相同的算法比較新值和舊值來(lái)確定變化佳遣。

注意

(這在傳遞對(duì)象作為 value 時(shí)會(huì)引發(fā)一些問(wèn)題Caveats.)

動(dòng)態(tài) Context

一個(gè)更加復(fù)雜的例子:

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

const ThemeContext = React.createContext({
  background: 'red',
  color: 'white'
});

通過(guò)靜態(tài)方法React.createContext()創(chuàng)建一個(gè)Context對(duì)象识埋,這個(gè)Context對(duì)象包含兩個(gè)組件,<Provider /><Consumer />苍日。

class App extends React.Component {
  render () {
    return (
      <ThemeContext.Provider value={{background: 'green', color: 'white'}}>
        <Header />
      </ThemeContext.Provider>
    );
  }
}

復(fù)制代碼<Provider />的value相當(dāng)于現(xiàn)在的getChildContext()惭聂。

class Header extends React.Component {
  render () {
    return (
      <Title>Hello React Context API</Title>
    );
  }
}
class Title extends React.Component {
  render () {
    return (
      <ThemeContext.Consumer>
        {context => (
          <h1 style={{background: context.background, color: context.color}}>
            {this.props.children}
          </h1>
        )}
      </ThemeContext.Consumer>
    );
  }
}

復(fù)制代碼<Consumer />children必須是一個(gè)函數(shù),通過(guò)函數(shù)的參數(shù)獲取<Provider />提供的Context相恃。

幾個(gè)可以直接獲取Context的地方

實(shí)際上辜纲,除了實(shí)例的context屬性(this.context),React組件還有很多個(gè)地方可以直接訪問(wèn)父組件提供的Context。比如構(gòu)造方法:

constructor(props, context)

比如生命周期:

componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componetWillUpdate(nextProps, nextState, nextContext)

對(duì)于面向函數(shù)的無(wú)狀態(tài)組件耕腾,可以通過(guò)函數(shù)的參數(shù)直接訪問(wèn)組件的Context见剩。

const StatelessComponent = (props, context) => (
  ......
)

作用于多個(gè)上下文

為了保持 context 快速進(jìn)行二次渲染, React 需要使每一個(gè) Consumer 在組件樹(shù)中成為一個(gè)單獨(dú)的節(jié)點(diǎn)扫俺。

// 主題上下文, 默認(rèn)light
const ThemeContext = React.createContext('light');

// 登陸用戶上下文
const UserContext = React.createContext();

// 一個(gè)依賴于兩個(gè)上下文的中間組件
function Toolbar(props) {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

class App extends React.Component {
  render() {
    const {signedInUser, theme} = this.props;

    // App組件提供上下文的初始值
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Toolbar />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

如果兩個(gè)或者多個(gè)上下文的值經(jīng)常被一起使用苍苞,也許你需要考慮你自己渲染屬性的組件提供給它們。

實(shí)例

import React, { Component } from 'react'

const LocalContext = React.createContext();

const { Provider, Consumer } = LocalContext; 

function Container(props) {
  return <Title />
}

function Title(props) {
  return (
    <div>
      <Consumer>
        { context => {
          return (
            <div>
              {context.name} - { context.age}
            </div>
          )
        }}
      </Consumer>
    </div>
  )
}

class DM extends Component {
  componentDidMount() {
    console.log(this.props);
  }

  render() {
    return (
      <div>
        <Consumer>
          {
            user => (<div>
              <p>{ user.name }</p>
              <p>{ user.age }</p>
            </div>
            )
          }
        </Consumer>
        ---{ this.props.name }----
      </div>
    )
  }

}

class ContextDemo extends Component {
  constructor (props, context) {
    super(props, context)
    this.state = {
      User: {
        age: 19,
        name: 'aicoder'
      }
    }
  }

  render () {
    return (
      <div>
        <Provider value={ this.state.User }>
          <Container></Container>
          <DM></DM>
        </Provider>
        <hr/>
        <input
          onClick={
            () => this.setState(preState => {
              return {User: { ...preState.User, age: preState.User.age + 1 }}
            })
          }
          className="button is-primary"
          value={ this.state.User.name }
          type="button"
          />
      </div>
    )
  }
}

export default ContextDemo

關(guān)注老馬

AICODER 官網(wǎng) React教程視頻地址

參考

  1. 官網(wǎng)文檔
  2. 老馬React視頻地址: https://ke.qq.com/course/379234?tuin=1eb4a0a4
  3. AICODER官網(wǎng)地址:https://www.aicoder.com/
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狼纬,一起剝皮案震驚了整個(gè)濱河市羹呵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疗琉,老刑警劉巖冈欢,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異盈简,居然都是意外死亡凑耻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)柠贤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)香浩,“玉大人,你說(shuō)我怎么就攤上這事臼勉×诳裕” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵坚俗,是天一觀的道長(zhǎng)镜盯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)猖败,這世上最難降的妖魔是什么速缆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮恩闻,結(jié)果婚禮上艺糜,老公的妹妹穿的比我還像新娘。我一直安慰自己幢尚,他們只是感情好破停,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著尉剩,像睡著了一般真慢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上理茎,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天黑界,我揣著相機(jī)與錄音管嬉,去河邊找鬼。 笑死朗鸠,一個(gè)胖子當(dāng)著我的面吹牛蚯撩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烛占,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胎挎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了忆家?” 一聲冷哼從身側(cè)響起犹菇,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弦赖,沒(méi)想到半個(gè)月后项栏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體浦辨,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹬竖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了流酬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片币厕。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芽腾,靈堂內(nèi)的尸體忽然破棺而出旦装,到底是詐尸還是另有隱情,我是刑警寧澤摊滔,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布阴绢,位于F島的核電站,受9級(jí)特大地震影響艰躺,放射性物質(zhì)發(fā)生泄漏呻袭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一腺兴、第九天 我趴在偏房一處隱蔽的房頂上張望左电。 院中可真熱鬧,春花似錦页响、人聲如沸篓足。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)栈拖。三九已至,卻和暖如春没陡,著一層夾襖步出監(jiān)牢的瞬間涩哟,已是汗流浹背烟瞧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留染簇,地道東北人参滴。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锻弓,于是被迫代替她去往敵國(guó)和親砾赔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容