shiningjason/react-quick-tutorial

Level 6: 該一層一層傳下去嗎?

Closed this issue · 1 comments

TodoHeader.js裡的

<h1>{title}</h1>

TodoApp.js


        <TodoHeader
          title="我的待辦清單"
          username="Jason"
          todoCount={99}
        />

傳入去,

那如果再由出一層的index.html傳入去,是不是要改為
TodoApp.js

        <TodoHeader
          title="{toDoHeaderTitle}"
          username="Jason"
          todoCount={99}
        />

index.html

    const props = {
      title: '我的待辦清單'
    };
    ReactDOM.render(
      <TodoApp {...props} />,
      document.getElementById('app')
    );

有更好的做法嗎?

嗨,@kalun1988,這問題有下列五種方式可以解決:

ㄧ. 如同你的做法,一層一層傳下去

好處是比較明確,不過太多層會迷路!

二. 將一整包 props 傳下去

好處是 TodoApp 不須特別再寫一次要傳遞哪些 props;不過底層元件要追有哪些 props 時,還是會迷路!

/* index.js */
ReactDOM.render(
  <TodoApp title="我的待辦清單" username="Jason" todoCount={99} />,
  document.getElementById('app')
);

/* TodoApp.js */
render() {
  return (
    <div>
      <TodoHeader {...this.props} />
    </div>
  )
}

/* TodoHeader.js */
render() {
  return (
    <div>
      <h1>{this.props.title}</h1>
    </div>
  )
}
補充 1. 你也可以在 TodoApp 中,覆蓋上層元件傳遞的 props
/* TodoApp.js */
render() {
  return (
    <div>
      <TodoHeader {...this.props} title="工作清單" />
    </div>
  )
}
補充 2. 或是在 TodoApp 中,給予預設的 props
/* TodoApp.js */
render() {
  return (
    <div>
      <TodoHeader title="預設清單" {...this.props} />
    </div>
  )
}

三. 使用 Object rest properties 將 props 拆解

這方法通常用於你不想把多餘的 props 傳遞到子元件時!

/* index.js */
ReactDOM.render(
  <TodoApp title="我的待辦清單" username="Jason" todoCount={99} todos={[]} />,
  document.getElementById('app')
);

/* TodoApp.js */
render() {
  // 如果我不想讓 TodoHeader 接到 todos 時
  const { todos, ...rest } = this.props;
  return (
    <div>
      <TodoHeader {...rest} />
    </div>
  )
}

/* TodoHeader.js */
render() {
  return (
    <div>
      <h1>{this.props.title}</h1>
    </div>
  )
}

四. 使用 context

這做法通常用於你是要傳遞整個應用程式共用的資料或函數,例如:theme, 多國語言包, 登入/登出, navigation 等。

/* TodoApp.js */
class TodoApp extends React.Component {
  getChildContext() {
    return {
      username: 'Jason' // 1. 假設登入者名稱會用在很多底層元件
    };
  }
  // ...
}

TodoApp.childContextTypes = {
  username: React.PropTypes.string
};

/* TodoHeader.js */
// 2. 假設 TodoHeader 是在 TodoApp -> ... -> ... -> 好幾層元件以下

class TodoHeader extends React.Component {
  render() {
    return (
      <div>
        <h1>{this.context.username}</h1>
      </div>
    );
  }
}

TodoHeader.contextTypes = {
  username: React.PropTypes.string
};

Ps. 不過官方不推薦該用法,因為唯有明確的傳遞 props 才會好維護!

參考連結

五. 實務用法

當你使用 Flux or Redux 和 container pattern 後~

class TodoApp extends React.Component {
  render() {
    // 1. 不用傳遞 props:
    // 使用 Flux or Redux 後,所有業務資料和應用狀態將會存在它們系統中,而不是上層元件
    return <TodoHeaderContainer />;
  }
}

/* TodoHeaderContainer.js */
// 2. 加入 Container 元件:
// Container 元件用在跟 Flux or Redux 同步資料,
// 並且存在自身的 state 中,
// 然後每次 render 將 state 資料再傳遞到 View 元件中
class TodoHeaderContainer extends React.Component {
  // ...省略詳細用法
  render() {
    return <TodoHeader title={this.state.title} />;
  }
}

/* TodoHeader.js */
class TodoHeader extends React.Component {
  render() {
    return (
      <div>
        <h1>{this.props.title}</h1>
      </div>
    );
  }
}

所以之後實際專案中,架構是長這樣的:

├── TodoApp.js
│   ├── TodoHeaderContainer.js
│   │   ├── TodoHeader.js
│   ├── TodoListContainer.js
│   │   ├── TodoList.js
│   ├── Footer.js
│   │   ├── TodoHeaderContainer.js
│   │   │   ├── TodoHeader.js

好處是

  1. TodoHeader, TodoList 等 View 元件的職責,只有拿 props 顯示資料
  2. TodoHeaderContainer, TodoListContainer 等 container 元件的職責,就是跟與 Flux or Rdeux 拿資料,並且傳遞資料給下層元件;所以當往後拿資料的方式變了,你只需換掉 container 元件即可

_回到你的問題,如果只是這樣簡單的範例,我認為目前一層一層傳下去是比較適當的!_

PS.
希望有幫助到你!也謝謝你提供我 feedback~!
如果上方有任何不明瞭的地方,歡迎你再給我意見囉!