依照官方文檔和例程博客,實現(xiàn)了1個簡單的輸入框組件。
如果想了解官方案例,請參考深入理解 React
總結1下,1個簡單的React.js利用應依照以下步驟構建:
個人認為這里的難點在于如何拆分用戶界面,粒度過大不利于組件化的重用,粒度太小的話,顯得冗余過量,并且復雜度陡升。本人最開始嘗試細化的拆分,盡可能讓1個組件只完成1個很小的功能需求,但最后反而不知道如何給每一個組件分配其任務。
另外一個難點來源于對state的判斷,其實如果能夠辨認出最小的state,讀者會發(fā)現(xiàn)1個復雜的組件所需的state數(shù)量其實很少。
在React.js中,有兩種數(shù)據(jù)模型,state代表的是會動態(tài)變化的狀態(tài),props代表的是父級傳遞而來的屬性,state會反過來更新UI,而props只是1次性設置填充。
我們的組件要完成以下功能:
原型比較簡單,樣式以下:
JSON接口數(shù)據(jù)以下:
{
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
buttonImgSrc:
regExp: /^\w{5,8}$/
}
我們通過JSON數(shù)據(jù)設置輸入框組件的label標簽、Input的placeholder屬性和對用戶輸入的正則驗證。
在完成用戶界面切分的進程中,最開始,如圖原型圖所示,做了非常細粒度的拆分,拆分結構以下:
這里,將LimitedInputBox
作為最外面的容器,包裹全部組件,子組件分為3部份,Title
即是1個label
標簽,用來顯示該輸入框組件的名稱(或說標題),InputBox
即是1個input
標簽,用來接收用戶輸入,ClearButton
可以用1個img
標簽,用來清除input
輸入框內容。剛開始準備用另外1個容器包裹InputBox
和ClearButton
,發(fā)現(xiàn)這樣過于冗余因而二者還是和Title
作為同1級組件比較好。
后來思來,覺得過于拆分了,最小單位與HTML原生標簽同1級別,沒成心義,因此,最后只保存了1個組件,就稱之為LimitedInputBox
。即:
首先,不需要斟酌用戶交互,直接將數(shù)據(jù)模型渲染到UI上。行將數(shù)據(jù)渲染和用戶交互兩個進程拆分開來。
這樣做比較簡單,由于構建靜態(tài)版本的頁面只需要大量的輸入,而不需要思考;但是添加交互功能卻需要大量的思考和少許的輸入。
為了創(chuàng)建1個渲染數(shù)據(jù)模型的利用的靜態(tài)版本,你將會構造1些組件,這些組件重用其它組件,并且通過 props 傳遞數(shù)據(jù)。 props 是1種從父級向子級傳遞數(shù)據(jù)的方式。
本例中,的props
就是第1步構建的JSON數(shù)據(jù)。
斟酌到第2步中我采取了兩種拆分方式,這里給出相應的代碼:
/** author : River He**/
//細分結構
var data = {
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
buttonImgSrc: "eliminate.png",
regExp: /^\w{5,8}$/
};
var Title = React.creatClass({
render: function() {
return (
<label>{this.props.labelText}</label>
);
}
});
var InputBox = React.createClass({
render: function() {
return (
<input
placeholder={this.props.hinderText}
regExp={this.props.regExp}>
</input>
);
}
});
var ClearButton = React.createClass({
render: function() {
return (
<img src={this.props.buttonImgSrc}></>
);
}
});
var LimitedInputBox = React.createClass({
render: function() {
return (
<Title labelText={this.props.data.labelText} />
<InputBox
hinderText={this.props.data.hinderText}
regExp={this.props.data.regExp}
/>
<ClearButton buttonImgSrc={this.props.data.buttonImgSrc} />
);
}
});
ReactDOM.render(
<LimitedInputBox data={data} />,
document.getElementById('example')
);
/** author : river he**/
//粗劃分
var data = {
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
buttonImgSrc: "eliminate.png",
regExp: /^\w{5,8}$/
};
var LimitedInputBox = React.createClass({
render: function() {
return (
<div>
<label >{this.props.data.labelText}</label>
<input
placeholder={this.props.data.hinderText}
regExp={this.props.data.regExp}>
</input>
<img src={this.props.buttonImgSrc ></img>
</div>
);
}
});
ReactDOM.render(
<LimitedInputBox data={data} />,
document.getElementById('example')
);
為了使 UI 可交互,需要能夠觸發(fā)底層數(shù)據(jù)模型的變化。 React 通過 state 使這變得簡單。
為了正確構建利用,首先需要斟酌利用需要的最小的可變 state 數(shù)據(jù)模型集合。此處關鍵點在于精簡:不要存儲重復的數(shù)據(jù)。構造出絕對最小的滿足利用需要的最小 state 是有必要的,并且計算出其它強烈需要的東西。
本案例較簡單,state
很明顯是inputText
,即input
輸入框中的值。
而對1般情況,可以簡單地對每項數(shù)據(jù)提出3個問題:
- 是不是是從父級通過props傳入的?如果是,可能不是state。
- 是不是會隨著時間改變?如果不是,可能不是state。
- 能夠根據(jù)組件中其他的state數(shù)據(jù)或props計算出來嗎?如果是,就不是state。
找出了state
以后,需要繼續(xù)找出那些組件會被state
更新,即哪些組件應當具有state
數(shù)據(jù)模型,本案例一樣很簡單,由于輸入框input
要判斷用戶輸入是不是符合正則表達式要求,因此與input
相干的組件都應當具有state
。依照第1種細分,InputBox
組件應當具有state
,依照第2個細分,由于只存在1個組件LimitedInputBox
,故其應當具有state
。
//細分結構
var data = {
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
buttonImgSrc: "eliminate.png",
regExp: /^\w{5,8}$/
};
var Title = React.creatClass({
render: function() {
return (
<label>{this.props.labelText}</label>
);
}
});
var InputBox = React.createClass({
render: function() {
return (
<input
placeholder={this.props.hinderText}
value={this.props.inputText}
regExp={this.props.regExp}>
</input>
);
}
});
var ClearButton = React.createClass({
render: function() {
return (
<img src={this.props.buttonImgSrc}></>
);
}
});
var LimitedInputBox = React.createClass({
getInitialState: function() {
return {
inputText: ''
};
},
render: function() {
return (
<Title labelText={this.props.data.labelText} />
<InputBox
hinderText={this.props.data.hinderText}
inputText={this.state.inputText}
regExp={this.props.data.regExp}
/>
<ClearButton buttonImgSrc={this.props.data.buttonImgSrc} />
);
}
});
ReactDOM.render(
<LimitedInputBox data={data} />,
document.getElementById('example')
);
/** author : river he**/
//粗劃分
var data = {
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
buttonImgSrc: "eliminate.png",
regExp: /^\w{5,8}$/
};
var LimitedInputBox = React.createClass({
//初始化state
getInitialState: function() {
return {
inputText: ''
};
},
render: function() {
return (
<div>
<label >{this.props.data.labelText}</label>
<input
placeholder={this.props.data.hinderText}
regExp={this.props.data.regExp}
value={this.state.inputText}
></input>
<img
src={this.props.data.buttonImgSrc}
</img>
</div>
);
}
});
ReactDOM.render(
<LimitedInputBox data={data} />,
document.getElementById('example')
);
前面構建了渲染正確的基于props
和state
的沿著組件樹從上至下單項數(shù)據(jù)活動的利用。然后我們需要構建反向的數(shù)據(jù)活動方式:組件樹中層級很深的表單組件更新父級中的state
。
如果嘗試在前面的輸入框中輸入,React會疏忽你的輸入。這是成心的,由于已設置了input
的value
屬性,使其總是和LimitedInputBox
(對粗分的情況,就是其本身)傳遞過來的state
1致。
但是我們希望的是,不管用戶什么時候改變了表單,都要更新state
來反利用戶的輸入。由于組件只能更新自己的state
,LimitedInputBox
將會傳遞1個回調函數(shù)給InputBox
,此函數(shù)將會在state
應當被改變時觸發(fā)。我們可使用input
的onChange
事件來監(jiān)聽用戶輸入,從而肯定什么時候觸發(fā)回調函數(shù)。
LimitedInputBox
傳遞的回調函數(shù)將會調用setState()
,然后利用將會被更新。
/**author : River He*/
//細分結構
var data = {
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
buttonImgSrc: "eliminate.png",
regExp: /^\w{5,8}$/
};
var Title = React.createClass({
render: function() {
return (
<label>{this.props.labelText}</label>
);
}
});
var InputBox = React.createClass({
handleChange: function() {
this.props.onUserInput(
this.refs.inputBox.value
);
},
fitRegExp: function(inputText) {
if(inputText.match(this.props.regExp) != null) {
// console.log("true");
return true;
} else {
// console.log("false");
return false;
}
},
render: function() {
return (
<input
style={this.fitRegExp(this.props.inputText)?({border: '1px solid green'}):({border: '1px solid red'})}
placeholder={this.props.hinderText}
value={this.props.inputText}
regExp={this.props.regExp}
ref="inputBox"
onChange={this.handleChange}>
</input>
);
}
});
var ClearButton = React.createClass({
handleClick: function() {
this.props.onClear();
},
render: function() {
return (
<img
src={this.props.buttonImgSrc}
onClick={this.handleClick}
/>
);
}
});
var LimitedInputBox = React.createClass({
getInitialState: function() {
return {
inputText: ''
};
},
handleUserInput: function(inputText) {
this.setState({
inputText: inputText
});
},
handleClear: function() {
this.delText();
},
getText: function() {
return this.state.inputText;
},
setText: function(text) {
this.setState({
inputText: text
});
},
delText: function() {
this.setState({
inputText: ''
});
},
render: function() {
return (
<div>
<Title labelText={this.props.data.labelText} />
<InputBox
hinderText={this.props.data.hinderText}
inputText={this.state.inputText}
regExp={this.props.data.regExp}
onUserInput={this.handleUserInput}
/>
<ClearButton
buttonImgSrc={this.props.data.buttonImgSrc}
onClear={this.handleClear}
/>
</div>
);
}
});
ReactDOM.render(
<LimitedInputBox data={data} />,
document.getElementById('example')
);
/** author : river he**/
//粗分結構
var data = {
labelText: "酒店地址",
hinderText: "請?zhí)顚懢频甑刂?,
regExp: /^\w{5,8}$/
};
var LimitedInputBox = React.createClass({
//初始化state
getInitialState: function() {
return {
inputText: ''
};
},
// handleKeyUp: function(inputText) {
// this.setState({
// inputText: this.refs.input.value.trim()
// });
// },
//處理輸入框變化
handleChange: function() {
this.setState({
inputText: this.refs.input.value.trim()
});
},
//處理刪除事件
handleClear: function() {
this.delText();
},
//刪除方法
delText: function() {
// console.log(this.refs.input.text);
this.setState({
inputText: ''
});
},
//獲得用戶輸入
getText: function() {
return this.state.inputText;
},
//設置輸入框內容
setText: function(text) {
this.setState({
inputText: text.trim()
});
},
//判斷輸入內容否符合設置的正則表達式
fitRegExp: function(inputText) {
// console.log("start fitRegExp");
if(inputText.match(this.props.data.regExp) != null) {
// console.log("true");
return true;
} else {
// console.log("false");
return false;
}
},
render: function() {
return (
<div>
<label >{this.props.data.labelText}</label>
<input
style={this.fitRegExp(this.state.inputText)?
({border: '1px solid green'}):({border: '1px solid red'})}
placeholder={this.props.data.hinderText}
regExp={this.props.data.regExp}
// onKeyUp={this.handleKeyUp}
onChange={this.handleChange}
value={this.state.inputText}
ref='input'
></input>
<img src="eliminate.png" onClick={this.handleClear}></img>
</div>
);
}
});
ReactDOM.render(
<LimitedInputBox data={data} />,
document.getElementById('example')
);
以上就是1個簡單的輸入框組件,相應的html模板和css以下,沒有甚么樣式,希望讀者體諒,有時間改下css。
<!-- limitedInputBox.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF⑻">
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
<!-- <script type="text/babel" src="http://www.vxbq.cn/upload/caiji/20160629/limitedInputBox.js"></script> -->
<script src="inputWidget.js" type="text/babel"></script>
<link rel="stylesheet" href="limitedInputBox.css">
<title>LimitedInputBox</title>
</head>
<body>
<div id="example"></div>
</body>
</html>
//limitedInputBox.css
img {
height: 14px;
width: 14px;
}
JS Bin on jsbin.com