前言
前面两节实战中,我们已经完成了三个主要组件及其内部组件,但是我们展示的数据都是写死的内容,我们期望内容是实时更新的。
我们需要引入状态的概念,在 React 内组件可以通过维护 state 来维护组件内的状态,这意味着 state 只关心组件自己的内部状态,且这些状态只能在组件内改变。
state
定义 state 的方式很简单:
class Demo extends React.Component {constructor(props) {super(props);this.state = {name: 'frank',};}}
现在有新提案,对实例属性有了新的写法(需要 babel 转义)
class Demo extends React.Component {state = {name: 'frank',};}
我们希望 state 是可以更改的,因为我们需要使用变化的 state 去实现动态可交互的组件。
怎么修改 state 呢?直接赋值?this.state.name = 'cxy'。
no no no,永远不要直接修改 state,这样做 React 并不会做出你所期望的操作,还有可能带来一些意想不到的 bug。
正确的操作是使用 setState 修改 state。
setState
setState 是一个异步方法,例如:
// state = {count: 0}console.log(this.state.count); // => 0this.setState({count: this.state.count + 1,});console.log(this.state.count); // => 0
出现这种情况是因为 react 并不会马上修改 state,而是将这个对象放入队列,且一个生命周期(生命周期相关内容可看 组件生命周期 章节)内所有的 setState 方法都会合并操作。
因此你并不需要担心写多个 setState 带来性能问题。
回过头来看上述例子,setState 允许我们传入第二个参数作为更新完 state 之后的回调,在这里我们可以获取到更新后的 state:
console.log(this.state.count) // => 0this.setState({count: this.state.count + 1,}, () => {console.log(this.state.count) // => 1})
setState 接受函数参数
// state = {count: 0}this.setState({count: this.state.count + 1,});this.setState({count: this.state.count + 2,});
看上述例子,我们期望得到结果3,实际上得到了2。
原因是第二个 setState 内的 count 依赖于 this.state.count,由于 setState 异步执行,this.state.count + 2 里的 count 仍然是0。
为了解决这种情况,我们将 setState 第一个参数改为函数返回对象的形式:
this.setState(prevState => ({count: prevState.count + 1,}));this.setState(prevState => ({count: prevState.count + 2,}));
此时,prevState 即上一次 setState 的结果,这样我们就可以针对该结果进行操作,最后返回一个对象作为更新的 state。
总结
我们已经知道可以使用 state 配合 setState 维护组件内部状态,setState 可接受两个参数,且第一个参数可使用函数的形式 目的为获取上一次 setState 的结果。
使用 setState 可以使 React 变得充满想象力,然而在项目的复杂度不断提高的情况下,并不推荐滥用 setState,滥用 setState 将会是数据流变得混乱,程序维护成本增大。
实战
我们先改造 RealTime 组件。如下:
class RealTime extends React.Component {constructor() {super();this.state = {temp: -5,weather: '晴',windType: '南风',windLevel: '3级',humidity: 48,};}render() {const {temp, weather, windType, windLevel, humidity,} = this.state;return (<div className="RealTime"><div className="temp">{temp}</div><div className="weather">{weather}</div><div className="wind">{`${windType} ${windLevel}`}</div><div className="humidity">{`湿度 ${humidity}%`}</div></div>);}}
现在 RealTime 组件变成了这样:

好了组件已经能展示 state 的内容了,下一步我们来改变 state。
首先我们希望能获取真实的数据,所以我们需要从真实的天气 api 取到数据并赋值给 state。
注:此处使用网上获取的魅族天气的 api,仅供学习使用,严禁用于商业途径。
然后我们在组件上写一个方法 componentDidMount(此方法会在组件加载完毕之后立即执行,适合发送 http 请求) :
componentDidMount() {// 如遇跨域问题 `create-react-app` 生成的项目可在 `package.json` 内新添// "proxy": "http://aider.meizu.com"fetch('/app/weather/listWeather?cityIds=101240101').then(res => res.json()).then((res) => {if (res.code === '200' && res.value.length) {const { realtime } = res.value[0];const {temp, weather, wD: windType, wS: windLevel, sD: humidity,} = realtime;// 调用 setState 改变 statethis.setState({temp,weather,windType,windLevel,humidity,});}});}
好了 现在已经可以从天气 api 取得数据并渲染到组件内。
