專案需求,趁這時候也來好好學習一下React吧!
之前三大框架學習的是Vue,有機會可以好好的再把筆記補起來!
提醒 要對 ES6掌握度非常高尤其時Class
使用!
此篇文章是看官方文件教學,撰寫自己理解使用
目錄:
ToolChain 選用
- 如果你正在學習 React 或建立全新的
single-page
應用程式,請使用Create React App
。 - 如果你正在建立一個使用 Node.js 的
server-rendered
網頁,請使用Next.js
。 - 如果你正在建立一個靜態內容的網頁,請使用
Gatsby
。 - 如果你正在建立一個
component
函式庫或與現存程式碼倉庫進行接軌,請使用更靈活的Toolchain
。
安裝
npx create-react-app my-app
JSX 基本介紹
const element = <h1>你好,世界!</h1>;
這種寫法看似HTML但其實不是,這是JS的擴充語法JSX
由於 JSX 比較接近 JavaScript 而不是 HTML,React DOM 使用 camelCase 來命名屬性而不是使用慣有的 HTML 屬性名稱。
舉例來說:在 JSX 之中,class 變成了className
而 tabindex 變成了tabIndex
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
可以包含children。
畢竟他是擴充語法,所以是由Babel去擴充編譯的。
Babel 將 JSX 編譯為呼叫 React.createElement()
的程式。
所以上述的是這種方式的簡寫
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, World!'
);
JSX必須經由Babel去編譯,當然現在腳手架很多都封裝起來了,專案就看起來稍顯簡潔,也是最被人忽略的地方!
Components & Props
Function Component
function formatDate(date) {
return date.toLocaleDateString();
}
function Welcome(props) {
return ( // 可以包含children。
<h1>Hello, {props.name}</h1>
<div>{formatDate(props.date)}</div> // 可以用 {} 為 JavaScript 表達式
);
}
ReactDOM.render(
Welcome({name:'1231232'}),
document.getElementById('root')
);
Class Component
function formatDate(date) {
return date.toLocaleDateString();
}
class Welcome extends React.Component {
render() {
return ( // 可以包含children。
<h1>Hello, {this.props.name}</h1>
<div>{formatDate(props.date)}</div> // 可以用 {} 為 JavaScript 表達式
)
}
}
const element = <Welcome name="Sara" />; // Component 的字首須為大寫字母
ReactDOM.render(
element,
document.getElementById('root')
);
在開發過程中,一個 Page 通常由好多個 component
組成,所以基本上會先建立好 component
會是由 Class Component
建立一堆 Class
Props 是唯讀的
這是守則。
// Pure function (a and b 沒有重新設定)
function sum(a, b) {
return a + b;
}
// Not Pure function (account的內容被更改了!!)
function withdraw(account, amount) {
account.total -= amount;
}
React 是很彈性的,但有一條嚴格的規定:
所有的 React component 都必須像 Pure function 一般保護他的 props
State & lifecycle
官方這邊使用到時鐘的範例,對於lifecycle是一個不錯的範例
這邊希望製作一個可以自己更新時間的comportment
,而非使用props
將參數從父層往內帶入
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />, // props new Date() 將時間傳入
document.getElementById('root')
);
}
setInterval(tick, 1000);
// 這樣其實可以運作 只是畫面每次DOM render 從頭載入很耗資源
// 期望是做到 <Clock />
加入 State 到 Class
class Clock extends React.Component {
constructor(props) {
super(props); // 繼承 props
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
目前這樣 時間會卡在畫面上,接下來就要介紹 Component lifecycle
Component lifecycle
- 每當 Clock render 到 DOM 的時候,我們想要設定一個 timer。在 React 中稱為「mount」。
- 每當產生的 Clock DOM 被移除時,我們想要清除 timer。在 React 中稱為「unmount」。
class Clock extends React.Component {
constructor(props) {
super(props); // 繼承 props
this.state = {date: new Date()};
}
// mount
componentDidMount() {
this.timerID = setInterval(() => this.tick() ,1000);
}
// unmount
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({date: new Date()});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
正確的使用 State
- 請不要直接修改 State
this.state.comment = 'Hello'; // 錯誤
this.setState({comment: 'Hello'}); // 正確
- State 的更新可能是非同步的
this.setState({ counter: this.state.counter + this.props.increment }); // 錯誤
this.setState((state, props) => ({ counter: state.counter + props.increment })); // 正確
- State 的更新將會被 Merge 可以單獨更新某個state
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
// 串某api
fetchxxxx().then(response => { this.setState({ comments: response.comment })});
}
State 單向流
這通常被稱作為「上至下」或「單向」的資料流。
任何 state 總是由某個特定的 component 所擁有,任何從 state 得到的資料或 UI,state 只能影響在 tree「以下」的 component。
EventHandler
- 事件的名稱在 React 中都是 camelCase,而在 HTML DOM 中則是小寫。
<button onclick="activateLasers()">
Activate Lasers
</button>
- 事件的值在 JSX 中是一個 function,而在 HTML DOM 中則是一個 string。
<button onClick={activateLasers}>
Activate Lasers
</button>
Component
中呈現方式為
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
建議使用方式:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 為了讓 `this` 能在 callback 中被使用,這裡的綁定是必要的:
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
將參數傳給 Event Handler
在一個迴圈中,我們常常會需要傳遞一個額外的參數給 event handler。例如,如果 id 是每一行的 ID 的話,下面兩種語法都可行: \
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
以上這兩行程式是相同的。一個使用 arrow functions
,另一個則使用了Function.prototype.bind
。
官網主要概念後面章節略過,為EventHandler延伸