ajax请求
react的组件中,一般我们在 componentDidMount
事件中做ajax请求,并获得数据后修改state。
请求后台获取数据可以用任何的ajax库,建议使用: 原生的fetch
或者axios
库。
例如新闻案例:
import React, { Component } from 'react'
import axios from 'axios';
class NewsList extends Component {
constructor(opt) {
super(opt);
this.state = {
newsList: []
};
}
componentDidMount() {
// 发送ajax请求到后台并获取数据
axios
.get('/db.json')
.then(res => {
// console.log(res.data.news);
this.setState({newsList: res.data.news});
});
}
delNews(id) {
// 不模拟从后台ajax请求删除数据
// 直接在当前数组中移除数据。
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>编号</th>
<th>新闻标题</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之间的属性区别
React实现了一套与浏览器无关的DOM系统,兼顾了性能和跨浏览器的兼容性。在React和Html之间有许多属性的行为是不同的。
checked
checked
属性受类型为checkbox
或radio
的<input>
组件的支持。你可以用它来设定是否组件是被选中的。这对于构建受控组件很有用。与之相对defaultChecked
这是非受控组件的属性,用来设定对应组件首次装载时是否选中状态。
className
使用className
属性指定一个CSS类。这个特性适用于所有的常规DOM节点和SVG元素,比如<div>
,<a>
和其它的元素。
如果你在React中使用Web组件(这是一种不常见的使用方式),请使用class
属性来代替。
dangerouslySetInnerHTML
dangerouslySetInnerHTML
是React提供的替换浏览器DOM中的innerHTML
接口的一个函数。一般而言,使用JS代码设置HTML文档的内容是危险的,因为这样很容易把你的用户信息暴露给跨站脚本攻击.所以,你虽然可以直接在React中设置html的内容,但你要使用 dangerouslySetInnerHTML
并向该函数传递一个含有__html
键的对象,用来提醒你自己这样做很危险。例如:
function createMarkup() {
return {__html: 'First · Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
htmlFor
因为for
是在javascript中的一个保留字,React元素使用 htmlFor
代替。
onChange
onChange
事件的行为正如你所期望的:无论一个表单字段何时发生变化,这个事件都会被触发。我们故意不使用浏览器已有的默认行为,因为onChange
在浏览器中的行为和名字不相称,React依靠这个事件实时处理用户输入。
selected
selected
属性被<option>
组件支持。你可以使用该属性设定组件是否被选择。这对构建受控组件很有用。
style
style
属性接受一个JavaScript对象,其属性用小驼峰命名法命名,而不是接受CSS字符串。这和DOM中style
JavaScript 属性是一致性的,是更高效的,而且能够防止XSS的安全漏洞。例如:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
注意样式不会自动补齐前缀。为了支持旧的浏览器,你需要手动提供相关的样式属性:
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中访问DOM节点的属性保持一致性(例如 node.style.backgroundImage
)。供应商前缀除了ms
,都应该以大写字母开头。这就是为什么WebkitTransition
有一个大写字母W
。
React将自动添加一个"px"后缀到某些数字内联样式属性。如果你希望使用不同于"px"的其他单位,指定值为带渴望单位的字符串。例如:
// Result style: '10px'
<div style={{ height: 10 }}>
Hello World!
</div>
// Result style: '10%'
<div style={{ height: '10%' }}>
Hello World!
</div>
不是所有样式属性被转化为像素字符串,尽管如此。某些个保持无单位(例如 zoom
, order
, flex
)。A complete list of 无单位属性 can be seen here.
value
value
属性受到<input>
和 <textarea>
组件的支持。你可以使用它设置组件的值。这对构建受控组件非常有用。defaultValue
属性对应的是非受控组件的属性,用来设置组件第一次装载时的值。
其他受支持的HTML属性
As of React 16, 任何标准的或自定义的 DOM属性都被充分支持。
React 总是提供一个以 JavaScript为中心的API给DOM。因为React组件对于自定义和DOM相关的属性都经常采用。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
这些属性的工作类似于对应的HTML属性,除了上述文档的特例。
(Context)上下文
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
在一个典型的 React 应用中,数据是通过 props 属性由上向下(由父及子)的进行传递的,但这对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI主题),这是应用程序中许多组件都所需要的。 Context 提供了一种在组件之间共享此类值的方式,而不必通过组件树的每个层级显式地传递 props
。
简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。
- 使用props或者state传递数据,数据自顶下流。
- 使用Context,可以跨越组件进行数据传递
何时使用 Context
Context 设计目的是为共享那些被认为对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。例如,在下面的代码中,我们通过一个“theme”属性手动调整一个按钮组件的样式:
function ThemedButton(props) {
return <Button theme={props.theme} />;
}
// 中间组件
function Toolbar(props) {
// Toolbar 组件必须添加一个额外的 theme 属性
// 然后传递它给 ThemedButton 组件
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
使用 context, 我可以避免通过中间元素传递 props:
// 创建一个 theme Context, 默认 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>
);
}
}
注意
不要仅仅为了避免在几个层级下的组件传递 props 而使用 context,它是被用于在多个层级的多个组件需要访问相同数据的情景。
React.createContext
const {Provider, Consumer} = React.createContext(defaultValue);
创建一对 { Provider, Consumer }
。当 React 渲染 context 组件 Consumer 时,它将从组件树的上层中最接近的匹配的 Provider 读取当前的 context 值。
如果上层的组件树没有一个匹配的 Provider,而此时你需要渲染一个 Consumer 组件,那么你可以用到 defaultValue
。这有助于在不封装它们的情况下对组件进行测试。
Provider
<Provider value={/* some value */}>
React 组件允许 Consumers 订阅 context 的改变。
接收一个 value
属性传递给 Provider 的后代 Consumers。一个 Provider 可以联系到多个 Consumers。Providers 可以被嵌套以覆盖组件树内更深层次的值。
Consumer
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
一个可以订阅 context 变化的 React 组件。
接收一个 函数作为子节点. 函数接收当前 context 的值并返回一个 React 节点。传递给函数的 value
将等于组件树中上层 context 的最近的 Provider 的 value
属性。如果 context 没有 Provider ,那么 value
参数将等于被传递给 createContext()
的 defaultValue
。
注意
关于此案例的更多信息, 请看 render props.
每当Provider的值发生改变时, 作为Provider后代的所有Consumers都会重新渲染。 从Provider到其后代的Consumers传播不受shouldComponentUpdate方法的约束,因此即使祖先组件退出更新时,后代Consumer也会被更新。
通过使用与Object.is相同的算法比较新值和旧值来确定变化。
注意
(这在传递对象作为
value
时会引发一些问题Caveats.)
动态 Context
一个更加复杂的例子:
import React from 'react';
import ReactDOM from 'react-dom';
const ThemeContext = React.createContext({
background: 'red',
color: 'white'
});
通过静态方法React.createContext()
创建一个Context
对象,这个Context对象包含两个组件,<Provider />
和<Consumer />
。
class App extends React.Component {
render () {
return (
<ThemeContext.Provider value={{background: 'green', color: 'white'}}>
<Header />
</ThemeContext.Provider>
);
}
}
复制代码<Provider />
的value相当于现在的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>
);
}
}
复制代码<Consumer />
的children
必须是一个函数,通过函数的参数获取<Provider />
提供的Context
。
几个可以直接获取Context的地方
实际上,除了实例的context
属性(this.context
),React
组件还有很多个地方可以直接访问父组件提供的Context
。比如构造方法:
constructor(props, context)
比如生命周期:
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componetWillUpdate(nextProps, nextState, nextContext)
对于面向函数的无状态组件,可以通过函数的参数直接访问组件的Context。
const StatelessComponent = (props, context) => (
......
)
作用于多个上下文
为了保持 context 快速进行二次渲染, React 需要使每一个 Consumer 在组件树中成为一个单独的节点。
// 主题上下文, 默认light
const ThemeContext = React.createContext('light');
// 登陆用户上下文
const UserContext = React.createContext();
// 一个依赖于两个上下文的中间组件
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>
);
}
}
如果两个或者多个上下文的值经常被一起使用,也许你需要考虑你自己渲染属性的组件提供给它们。
实例
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