React 進階(三) 高階元件

難忘記發表於2019-03-18

元件化是近年來前端最重要的一個概念,其實他的本質是聚焦專案中複雜問題的分解。用比較抽象的話來說,就是用樂高積木的思想理解專案,搭建專案。即用無數個不同的小積木(功能簡單,結構單一),構建出來一個龐大的精巧的玩具(功能結構都很複雜)。要做到這一點需要將元件理解到最為透徹。

元件劃分的藝術

  • 元件的劃分,從最簡單出入手,可以按照層級劃分,即每一個專案都可以分為Header,Footer,Content這三大部分,然後Header可以分為Logo,HeaderText,以及HeaderMore,HeaderSearch等幾個部分,這是從最簡單視覺角度分拆,我們可以把它叫做橫向拆分吧。
  • 另一種拆分是更加對資料理解透徹之後產生的,就像一些後臺專案一樣,將要連結資料庫的操作分為Model層,在前端即可以理解為將ajax封裝起來,封為Model層,將業務邏輯封裝為Controller層,在前端就是將對ajax資料的操作分為Controller層,將最終資料的組裝,返回分為View層,在前端就是將展現分為View層,這種分層就是典型的MVC架構,只有將這種資料架構理解到深處,才可以真正的上手大型專案。
  • 我們這次就聚焦於React下的MVC架構,記錄這條路上應該如何走。
最簡單的元件組合
  • 容器元件和展示元件
    • 不知道大家有沒有注意觀察過淘寶網,它裡面有一部分資料是相同的,但是展現形式不同,那這個時候,我們就可以把元件拆開,分為容器元件,和展示元件,容器元件負責拿回來資料,而展示元件就負責展示,在這種情況下,我們可以有不同的展示元件,也可以有不用的容器元件,而我們開發的過程就更像組合積木的過程了
    • 容器元件的特點:
      • 更關心行為部分,即ajax,以及資料操作
      • 負責渲染對應的展示元件
      • 定義事件處理
      • 用class宣告
    • 表現元件的特點:
      • 更關心視覺展現
      • 負責渲染HTML
      • 用props從父元件接受資料
      • 通常是無狀態函式元件/pureComponent
// 關注點分離
// 表現元件
const ShowList = ({data})=>(<div>{data.map(item=>(<h3 key={item.id}>{item.text}</h3>))}</div>)
// 容器元件
class Game extends React.Component{
  constructor(){
    super();
    this.state = {
      data: []
    }
  }
  componentDidMount = () => {
    axios.get("http://localhost:8081")
    .then(res=>res.data)
    .then(data=>this.setState(data));
  }
  
  render(){
   //組裝
    return (<div className="container">
      <ShowList {...this.state}/>
      {/* {this.state.data.map(item=><h3 key={item.id}>{item.text}</h3>)} */}
    </div>)
  }
}
複製程式碼
  • React中的HOC
    • 當我們的一部分業務程式碼需要複用時,React官方在最開始推薦我們的方法是mixins,但這種方法很容易就導致程式碼mixins之間相互依賴,變數函式衝突等問題
    • 後來就有了高階元件,react官方也就推薦這種方式作為複用元件邏輯的官方方法,他並不是React官方API。而是由於元件組合特性衍生出來的一種設計模式,這種方法他可以很好的將功能整合,不影響外部環境,並且可以通過多層包裝完成minxin的效果,具體效果如下
// 高階元件,用元件生成元件
const BasicComponent = ({name, width})=>{
  return (<h1>我是{name},當前視窗寬度為{width}px</h1>)
};
const withWindowResize = RawComponent=>{
  return class NewComponent extends Component {
    constructor(props){
      super(props);
      this.state = {
        innerWidth:0
      }
    }
    componentDidMount = () => {
      this.setState({
        innerWidth: window.innerWidth
      });
    }
    componentWillMount = () => {
      window.addEventListener("resize", this.handelResize, false);
    }
    componentWillUnmount = () => {
      window.removeEventListener("resize", this.handelResize, false);
    }
    handelResize = ()=>{
      this.setState({
        innerWidth: window.innerWidth
      });
    }
    
    render(){
      return (<RawComponent name="xiaoming" width={this.state.innerWidth}></RawComponent>)
    }
  }
};
const Game = withWindowResize(BasicComponent);
export default Game;
複製程式碼

函式子元件

函式子元件可以理解為通過從props傳遞引數,然後在props.children定義函式,將引數處理後放入props.children執行這個函式。

class FetchWrapper extends Component {
  static propTypes = {
    children: PropTypes.func.isRequired,
    url: PropTypes.string.isRequired
  };
  constructor(props) {
    super(props);
    this.state = {
      data: []
    }
  }
  componentDidMount() {
    fetch(
      this.props.url
    )
      .then(response => response.json())
      .then((data) => this.setState(
        {data: data}
      ))
      .catch(console.error);
  }
  render() {
    return this.props.children(this.state.data)
  }
}
class App extends Component {
  constructor() {
    super();
  }

  render() {
    return (
      <div>
        <FetchWrapper url="https://api.github.com/users/gaearon/gists">
          {data => {
            return data.map(item => {
              return (
                <li style={{textAlign: 'left'}} onClick={this.clickList} key={item.id}>
                  <h5>
                    {item.id}
                  </h5>
                  <p>
                    {item.description || "no desc"}
                  </p>
                </li>
              )
            })
          }}
        </FetchWrapper>
      </div>
    )
  }
}
複製程式碼

CONTEXT 跨元件傳參

context主要用於元件中的跨層級傳參,他的底層就是使用了函式子元件,使用如下

const FirstContext = React.createContext("#");
class Game extends Component {
  constructor(){
    super();
    this.state = {
      
    }
  }
  render(){
    return (
     <div>
        // 供應商
        <FirstContext.Provider value="$">
            // 消費者
          <FirstContext.Consumer>
            {value=>[1,2,3,4].map(i=><h3 key={i}>{i*100}_{value}</h3>)}
          </FirstContext.Consumer>
        </FirstContext.Provider>
        <FirstContext.Consumer>
          {value=>[1,2,3,4].map(i=><h3 key={i}>{i*100}_{value}</h3>)}
        </FirstContext.Consumer>
     </div>
      
    )
  }
}
複製程式碼

相關文章