# 效果
## 分析
在這里面我們是在輸入框中輸入數據,按下enter鍵,對評論列表中添加一條新的數據。那么表單的數據和評論列表的數據都應該存放在state中。

# 新建組件
## 1.在index.html中引入bootstrap樣式
~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body style="background: skyblue">
<div id="root"></div>
</body>
</html>
~~~
## 2.新建Commet.js組件
新建`src/component/Commet.js`并書寫基本結構
~~~
import React, {Component} from 'react';
export default class Commet extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<div className={'container'} style={{marginTop:100}}>
<div className="form-group">
<input type="text" className="form-control"/>
</div>
<div className="list-group">
<li className="list-group-item">
111
<span className="close">×</span>
</li>
<li className="list-group-item">
111
<span className="close">×</span>
</li>
</div>
<div className="text-center bg-success">共有條評論</div>
</div>
);
}
}
~~~
# 雙向數據綁定
這里面給表單添加數據目的是為了將來給評論列表的數組中添加一些數據
~~~
changeUser(){
this.setState((state,props)=>{
return {
user:this.refs.my.value
}
})
}
……
<input ref="my"
value={this.state.user}
onChange={this.changeUser.bind(this)} type="text" className="form-control"/>
~~~
給評論列表的數組中添加一些新的數據
~~~
import React, {Component} from 'react';
export default class Commet extends Component {
constructor(props) {
super(props);
this.state = {
user: '',
list: []
}
}
changeUser() {
this.setState((state, props) => {
return {
user: this.refs.my.value
}
})
}
addList(e) {
if (e.keyCode === 13) {
this.setState((state, props) => {
console.log(state, props)
var newarr = JSON.parse(JSON.stringify(state.list))
newarr.unshift(state.user)
return {
list: newarr,
user: ''
}
})
}
}
removeItem(index) {
console.log(index)
this.setState((state,props)=>{
var newarr = JSON.parse(JSON.stringify(state.list))
newarr.splice(index,1)
return {
list:newarr
}
})
}
renderList() {
return this.state.list.map((item, index) => {
return (<li key={index} className="list-group-item">
{item}
<span className="close" onClick={this.removeItem.bind(this,index)}>×</span>
</li>)
})
}
render() {
return (
<div className={'container'} style={{marginTop: 100}}>
<div className="form-group">
<input ref="my"
value={this.state.user}
onChange={this.changeUser.bind(this)}
onKeyDown={this.addList.bind(this)}
type="text" className="form-control"/>
</div>
<div className="list-group">
{this.renderList()}
</div>
<div className="text-center bg-success">共有條評論</div>
</div>
);
}
}
~~~
## 注意事項
千萬不要對原數組進行直接操作, 直接操作只會影響數據的改變,并不會觸發視圖進行更新,如果期望數據和視圖同時更新必須使用this.setState()這個方法。
# 拆分組件
新建`src/components/Item.js`
~~~
import React, {Component} from 'react';
export default class Item extends Component {
constructor(props) {
super(props);
this.state = props;
}
removeItem(index) {
this.state.rem(index)
}
render() {
return (
<li className="list-group-item">
{this.state.item}
<span className="close" onClick={this.removeItem.bind(this, this.props.index)}>×</span>
</li>
);
}
}
~~~
修改Commet.js組件
~~~
removeItem(index) {
console.log(index)
this.setState((state, props) => {
var newlist = JSON.parse(JSON.stringify(state.list))
newlist.splice(index, 1)
return {
list: newlist
}
})
}
renderList() {
return this.state.list.map((item, index) => {
return (
<Item rem={this.removeItem.bind(this)} key={index} item={item} index={index}/>
)
})
}
~~~
在這里我們用子組件向父組件傳遞數據,不是用的自定義事件觸發,而是父組件將父組件對應的功能通過props傳遞到子組件中。子組件書寫功能觸發這個功能即可。
# 總結
**父子組件通信在React中是通過 props來進行傳遞信息和功能的。**
# 拔高
如果對自己有更高要求的可以看一下,如果自己感覺有點難,也是正常,那么可以先聽一下有一點點印象,不急于立即掌握。
## 修改Item組件
在子組件中有的同學喜歡把props的數據放到state中。這樣使用的話,就會統一了。不用一會props,一會state了。
Item組件修改如下:
~~~
import React, {Component} from 'react';
export default class Item extends Component {
constructor(props) {
super(props);
this.state = props;
}
removeItem(index) {
this.state.rem(index)
}
render() {
return (
<li className="list-group-item">
{this.state.item}
<span className="close" onClick={this.removeItem.bind(this, this.state.index)}>×</span>
</li>
);
}
}
~~~

會直接報錯,錯誤如下
```
Warning: Item: It is not recommended to assign props directly to state because updates to props won't be reflected in state. In most cases, it is better to use props directly.
```
直接props值賦給state是不可以的。還不如你直接使用props呢,而不是state。
修改:
~~~
constructor(props) {
super(props);
this.state = {
...props
};
}
~~~
通過屬性擴散將props的屬性擴展到state里面。終于不再報錯了。
## 再次嘗試效果

去們發現我們明明輸入的是01 02 03 但是顯示的卻全是01,這是為什么呀?
這里面如果我們數據添加使用的是push不會產生上面的問題,如果一旦用了unshift就會出現上面的問題。
首先constructor這個生命周期,默認只會執行一次。props中的數據也是擴展到state中的。數據都是拷貝過去的。
當父組件的props數據發生改變的時候子組件并未發生修改。那怎么辦呢?在子組件中有專門的父組件發生改變的生命周期`componentWillReceiveProps`在這個生命周期中讓state和最新props同步一下,這樣就可以了。
~~~
componentWillReceiveProps(nextProps, nextContext) {
this.setState((state,props)=>{
return nextProps
})
}
~~~

## 思考題 請問為什么顯示的都是01???拔高
這里面如果我們數據添加使用的是push不會產生上面的問題,如果一旦用了unshift就會出現上面的問題。看下圖。

看完圖相信大家有一定的理解了,其實說的更白一點不論父級屬性改不改,子組件默認都不會變,除非`componentWillReceiveProps`生命周期調用了`this.setState()`這個方法才會引起下次的`render`生命周期執行,這樣的話組件才會重新渲染成最新的數據。
# 總結
工作中到底和props好還是state好呢?這個還是看個人需求,有的人認為一會props一會state,不太好理解,那么就按照拔高里面的知識去做,通過擴散屬性將props的值拷貝到state中。不過也要注意一點如果props發生改變的情況,需要在`componentWillReceiveProps`生命周期中,執行`this.setState((state,props)=>{ return nextprops})`否則父組件傳遞的數據子組件不會重新渲染。
- webpack復習
- React基礎
- 前端三大主流框架對比
- React中幾個核心的概念
- React基礎語法
- React JSX語法
- React組件
- 普通組件
- 組件通信-父向子傳遞
- 組件拆成單個文件
- 面向對象復習
- Class組件基礎
- Class組件的私有狀態(私有數據)
- 案例:實現評論列表組件
- 組件樣式管理
- 組件樣式分離-樣式表
- CSS模塊化
- 生命周期
- React組件生命周期
- Counter組件來學習組件生命周期
- 生命周期總結
- 生命周期案例
- React評論列表
- React雙向數據綁定
- React版todolist
- 其它提高(了解)
- 組件默認值和數據類型驗證
- 綁定this并傳參的三種方式
- 祖孫級和非父子組件傳遞數據(了解)
- React路由
- 路由基礎
- 動態路由
- 路由嚴格模式
- 路由導航定位
- 路由重定向
- 路由懶加載
- WolfMovie項目
- 項目初始化
- AntDesign使用
- 其它相關了解