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
屬性受類型為checkbox
或radio
的<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 · 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中style
JavaScript 屬性是一致性的私股,是更高效的,而且能夠防止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ù)自頂下流院领。
- 使用Context弛矛,可以跨越組件進(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