返回
Featured image of post React - 主要概念

React - 主要概念

React - 主要概念,從頭學React

專案需求,趁這時候也來好好學習一下React吧!
之前三大框架學習的是Vue,有機會可以好好的再把筆記補起來!
提醒 要對 ES6掌握度非常高尤其時 Class 使用!
此篇文章是看官方文件教學,撰寫自己理解使用

目錄:


ToolChain 選用

  1. 如果你正在學習 React 或建立全新的 single-page 應用程式,請使用 Create React App
  2. 如果你正在建立一個使用 Node.js 的 server-rendered 網頁,請使用 Next.js
  3. 如果你正在建立一個靜態內容的網頁,請使用 Gatsby
  4. 如果你正在建立一個 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

  1. 請不要直接修改 State
this.state.comment = 'Hello';      // 錯誤
this.setState({comment: 'Hello'}); // 正確
  1. State 的更新可能是非同步的
this.setState({ counter: this.state.counter + this.props.increment }); // 錯誤
this.setState((state, props) => ({ counter: state.counter + props.increment })); // 正確
  1. 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延伸

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus