本案例來自React.js中文官網對應內容。 一. 運行環境 二. 組件架構 App下有兩個子組件 (評論列表)和 (評論區),其中 下又有一個子組件 (評論) Comment包括一個h2的評論人名稱,一個span的評論內容,獲取數據之後,Comment組件以數組的形式傳入CommentList。 ...
本案例來自React.js中文官網對應內容。
一. 運行環境
<link rel="stylesheet" type="text/css" href="css/css.css"/>
<!-- 核心 -->
<script src="js/react.js"></script>
<!-- 載入dom方法 -->
<script src="js/react-dom.js"></script>
<!-- 將 JSX 語法轉為 JavaScript 語法 -->
<script src="js/browser.min.js"></script>
<!-- jquery -->
<script type="text/javascript" src="js/jquery-1.11.3.js"></script>
<!--markdown渲染引擎-->
<script type="text/javascript" src="js/remarkable.min.js"></script>
<!-- 自身的javascript代碼 -->
<script type="text/javascript" src="js/js.js"></script>
</head>
二. 組件架構
App下有兩個子組件CommentList
(評論列表)和CommentForm
(評論區),其中CommentList
下又有一個子組件Comment
(評論)
- App
- CommentList
- Comment
- CommentForm
Comment包括一個h2的評論人名稱,一個span的評論內容,獲取數據之後,Comment組件以數組的形式傳入CommentList。
三. 流動數據
接下來就是把這5個組件寫出來。
// 最底層的組件Comment,隸屬於CommentList
var Comment=React.createClass({
render:function(){
return (
);
}
});
// 中層對象,包括CommentList和CommentForm
var CommentList=React.createClass({
render:function(){
return (
);
}
});
var CommentForm=React.createClass({
render:function(){
return (
);
}
});
// 最高層對象App,包括兩個中層組件
var App=React.createClass({
render:function(){
return (
);
}
});
// 渲染模塊
ReactDOM.render(
<App/>,
document.getElementById('example')
);
數據通過props傳入App,可以在html文件根目錄下創建一個“json.json”文件:放入以下內容:
[
{"id": 1, "author": "小明", "text": "小明摸摸大"},
{"id": 2, "author": "小紅", "text": "小紅萌萌噠"}
]
所以我只要給App傳遞一個url即可。然後在文檔插入之後執行jQuery的getJSON方法:最後在存入到App的state中。每次更新state,對會造成變化。
既然state可以實時更新,那麼不如設置一個定時器,每隔2s執行一次數據刷新。刷新間隔由App的props.timer傳入。
目前的組件變成了這樣
// 最底層的組件Comment,隸屬於CommentList
var Comment=React.createClass({
render:function(){
return (
<li>
<h2>{this.props.author}</h2>
<span>{this.props.text}</span>
</li>
);
}
});
//
// 中層對象,包括CommentList和CommentForm
var CommentForm=React.createClass({
render:function(){
//console.log(this.props)
return (
<div></div>
);
}
});
var CommentList=React.createClass({
render:function(){
var arr=[];
this.props.data.map(function(item){
//console.log(item.author)
var content=
(
<Comment
author={item.author}
text={item.text}
key={item.id}/>
);
arr.push(content);
});
console.log(arr)
return (
<ul>{arr}</ul>
);
}
});
// 最高層對象App,包括兩個中層組件
var App=React.createClass({
getInitialState:function(){
return {
data:[]
};
},
loadServer:function(){
var _this=this;
$.getJSON(this.props.url,function(data){
_this.setState({
data:data
})
});
},
componentDidMount:function(){
console.log(this.props.timer)
var _this=this;
setInterval(function(){
_this.loadServer();
},_this.props.timer)
},
render:function(){
//console.log(this.state)
return (
<div>
<h1>當前評論</h1>
<CommentList data={this.state.data}/>
<CommentForm/>
</div>
);
}
});
// 渲染模塊
ReactDOM.render(
<App url="json.json" timer={2000}/>,
document.getElementById('example')
);
數據就流動起來了。如果你修改了伺服器,定時器會在2s後重新更新所得到的數據。
四. markdown語法
這部分主要是調用remarkable插件,渲染你看到的數據。而渲染自然是在底層的Comment組件上完成。
所以我把Comment組件改成這樣:
// 最底層的組件Comment,隸屬於CommentList
var Comment=React.createClass({
render:function(){
var md=new Remarkable();//調用插件
var commentContent=md.render(this.props.text.toString());//渲染獲得的字元串!
return (
<li>
<h2>{this.props.author}</h2>
<span>{commentContent}</span>
</li>
);
}
});
結果就搞笑了。純文本在渲染引擎渲染之後變成了帶P標簽的內容。
解決方案:再此需要一個官方定義的props——
dangerouslySetInnerHTML
實現方法如下:
// 最底層的組件Comment,隸屬於CommentList
var Comment=React.createClass({
getContent:function(){
var md=new Remarkable();//調用插件
var commentContent=md.render(this.props.text.toString());//渲染獲得的字元串!
return ({
__html:commentContent
});//註意是兩個下劃線!
},
render:function(){
return (
<li>
<h2>{this.props.author}</h2>
<span dangerouslySetInnerHTML={this.getContent()}/>
</li>
);
}
});
五.添加新評論
到目前為止一切看起來還算簡單。現在需要加新的評論。
CommentForm添加的是一個新的表單。裡面包含了評論的大名,評論內容,還有提交按鈕。
var CommentForm=React.createClass({
render:function(){
//console.log(this.props)
return (
<form>
<input type="text" placeholder="你的大名"/><br/>
<textarea type="text" placeholder="評論內容"></textarea><br/>
<button type="submit">提交</button>
</form>
);
}
});
在實現這個組件的時候,需要考慮它的狀態。文本框的內容顯然是狀態。所以用一個state來保存它的狀態。輸入一次就保存一次並返回到文本框的value之中。
var CommentForm=React.createClass({
getInitialState:function(){
return ({
author:"",
text:""
});
},//初始化狀態
authorChange:function(e){
this.setState({
author:e.target.value
});
},
textChange:function(e){
this.setState({
text:e.target.value
})
},
render:function(){
//console.log(this.props)
return (
<form>
<input
type="text"
placeholder="你的大名"
value={this.state.author}
onChange={this.authorChange}/><br/>
<textarea type="text"
placeholder="評論內容"
value={this.state.text}
onChange={this.textChange}></textarea><br/>
<button type="submit">提交</button>
</form>
);
}
});
組件已經變得很長很長,但是基本上也不需要想什麼。
提交表單
提交表單做三件事,onSubmit在form標簽上。
- 阻止跳轉(e.preventDefault)
- 清空form的內容
- 返回到伺服器並刷新列表
<form onSubmit={this.submit}>...
submit函數:
...
submit:function(e){
e.preventDefault();
// 接下來格式化獲取內容!註意去掉評論頭尾的空格。
var authorStr=this.state.author.trim();
var textStr=this.state.text.trim();
if(!textStr||!authorStr){
alert('你有地方忘了填!');
return false;
}//簡單的表單校驗
this.setState({
author:'',
text:''
});//清空表單內容
},
...
這就實現了前兩步,但是提交到伺服器並刷新好像用前端的方法無法實現。
我們之前做了每隔兩秒鐘刷新一次,所以每次都要拿數據,在這個案例中不現實。
所以為了不涉及太多無關知識,把定時器拿掉。
先不理它,還是做回調函數,把狀態反饋到App:並刷新App的state:
返回的callback應該包括評論人名字,評論內容,說白了就是把這個對象扔進去作為參數。
那麼id值呢?就用一個Data對象給他生成吧!(Date.now())
// 最底層的組件Comment,隸屬於CommentList
var Comment=React.createClass({
getContent:function(){
var md=new Remarkable();//調用插件
var commentContent=md.render(this.props.text.toString());//渲染獲得的字元串!
return ({
__html:commentContent
});//註意是兩個下劃線!
},
render:function(){
return (
<li>
<h2>{this.props.author}</h2>
<span dangerouslySetInnerHTML={this.getContent()}/>
</li>
);
}
});
//
// 中層對象,包括CommentList和CommentForm
var CommentForm=React.createClass({
getInitialState:function(){
return ({
author:"",
text:""
});
},//初始化狀態
authorChange:function(e){
this.setState({
author:e.target.value,
});
},
textChange:function(e){
this.setState({
text:e.target.value,
})
},
submit:function(e){
e.preventDefault();
// 接下來格式化獲取內容!註意去掉評論頭尾的空格。
var authorStr=this.state.author.trim();
var textStr=this.state.text.trim();
if(!textStr||!authorStr){
alert('你有地方忘了填!');
return false;
}//簡單的表單校驗
this.props.callback({
"author":authorStr,
"text":textStr
});// 提交到伺服器!
this.setState({
author:'',
text:''
});//清空表單內容
},
render:function(){
//console.log(this.props)
return (
<form onSubmit={this.submit}>
<input
type="text"
placeholder="你的大名"
value={this.state.author}
onChange={this.authorChange}/><br/>
<textarea type="text"
placeholder="評論內容"
value={this.state.text}
onChange={this.textChange}></textarea><br/>
<button type="submit">提交</button>
</form>
);
}
});
var CommentList=React.createClass({
render:function(){
var arr=[];
this.props.data.map(function(item){
var content=
(
<Comment
author={item.author}
text={item.text}
key={item.id}/>
);
arr.push(content);
});
return (
<ul>{arr}</ul>
);
}
});
// 最高層對象App,包括兩個中層組件
var App=React.createClass({
getInitialState:function(){
return {
data:[]
};
},
loadServer:function(){
var _this=this;
$.getJSON(this.props.url,function(data){
_this.setState({
data:data
})
});
},
componentDidMount:function(){
this.loadServer();
// 伺服器環境設置
// var _this=this;
// setInterval(function(){
// _this.loadServer();
// },_this.props.timer)
},
refreshComment:function(comment) {
console.log(comment)//假設已經提交了
comment.id=Date.now()//給他一個id值!
var newComments=this.state.data.concat([comment]);//把它橋接起來!
console.log(newComments)
// 接下來再獲取一次。
// 伺服器環境下可以這樣做
// $.ajax({
// url: this.props.url,
// dataType: 'json',
// type: 'POST',
// data: comment,
// success: function(data) {
// //console.log(data)
// this.setState({data: data});
// }.bind(this),
// error: function(xhr, status, err) {
// console.error(this.props.url, status, err.toString());
// }.bind(this)
// });
//非伺服器環境設置一個新狀態就可以了。
this.setState({
data:newComments
})
},
render:function(){
//console.log(this.state)
return (
<div>
<h1>當前評論</h1>
<CommentList data={this.state.data}/>
<CommentForm callback={this.refreshComment}/>
</div>
);
}
});
// 渲染模塊
ReactDOM.render(
<App url="json.json" timer={2000}/>,
document.getElementById('example')
);
效果支持簡單的markdown語法: