React學(xué)習(xí)筆記(下)

內(nèi)容概要:

  • Higher order component
  • Ref
  • context API
  • Http / Ajax / axios
  • Redux


1. Higher-order component

A higher-order component is a function that takes a component and returns a new component.

Example1:

//Inside the withClass
import React from 'react';

// const WithClass = props => (    
//     <div className={props.classes}> {props.children} </div>
// );

//now it is not a Component any more
//It is a normal javascript function
const withClass = (WrappedComponent, className) => {
    return props => (
        <div className={className}>
            <WrappedComponent {...props}/>
        </div>
    )
}

export default withClass
// Use the higher order component in App.js
export default withClass(App, styles.App) ;



Example 2:

import React from 'react';
const Aux =  (props) => props.children;
export default Aux
//Use it in App.js
 return (
      <Aux>
        <button onClick={() => {this.setState({showCockpit: false});}}>Show Cockpit</button>
        {this.state.showCockpit? <Cockpit
          name = "SS"
          peopleLength = {this.state.people.length}
          showPersons = {this.state.showPerson}
          switch = {this.switchNameHandler}
          toggle = {this.togglePersonHandler}
          showCockpit = {this.state.showCockpit}
        /> : null}
        {persons}
      
      </Aux>
    );


2. Ref

Refs stand for references. ref keyword could be used on any element or component.

When to Use Refs:
There are a few good use cases for refs:

  • Managing focus, text selection, or media playback.
  • Triggering imperative animations.
  • Integrating with third-party DOM libraries.

Creating Refs

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}



Accessing Refs
When a ref is passed to an element in render, a reference to the node becomes accessible at the current attribute of the ref.

const node = this.myRef.current;



The value of the ref differs depending on the type of the node:

  • When the ref attribute is used on an HTML element, the ref created in the constructor with React.createRef() receives the underlying DOM element as its current property.
  • When the ref attribute is used on a custom class component, the ref object receives the mounted instance of the component as its current.
  • You may not use the ref attribute on function components because they don’t have instances.

current property 在首次mounted時(shí)賦值為當(dāng)前element抵窒,在unmounted時(shí)重新變?yōu)?code>null万皿。 ref的值在 componentDidMountcomponentDidUpdate之前更新

useRef()
在默認(rèn)情況下殴俱,functional component 中不能使用ref易结。 然而可以使用 useRef() React Hook 在functional component 中創(chuàng)建并初始化 ref.

const Cockpit = (props) => {
  const toggleBtnRef = useRef(null);

    //useEffect works after every render cycle
    // The functions inside will execute after the JSX loaded
    useEffect(() => {
        toggleBtnRef.current.click();
        
        return () => {
            console.log('[Cockpit.js] cleanup work in useEffect');
        }
    }, []);

  return(
        <div>
            <button
                className={btnClass}
                onClick={props.toggle}
                key = 'toggleBtn'
                ref = {toggleBtnRef}
            >TogglePerson</button>
        </div>
    );
}

以上例子中,使得button按鈕在首次render完成后瓢宦,被默認(rèn)點(diǎn)擊一次碎连。


3. Context API

Say we are in a case where we want to pass a prop from component A at the top to component D at the bottom. And we have components B and C in the middle, but they don't really care about the props, they will just transmit props.

It is useful when you don't want to pass props across multiple layers. It helps you skip the middle components.

import {React} from 'React';

const authContext = React.createContext({
    authenticated: false,
    login: () => {}
});

export default authContext;

React.createContext() 可以創(chuàng)建并初始化context的值,這個(gè)值可以是一個(gè) JavaScript Object, 也可以是 number, array, string等等驮履。你可以自定義這個(gè)值的有效范圍鱼辙, 讓他在React Components中傳遞。

  • Provider & Consumer
    在App.js中玫镐,將需要傳遞的值賦給在上例中已提前定義好的JS對象倒戏。使用<AuthContext.Provider></AuthContext.Provider> 包裹你需要使用此value的內(nèi)容。
import AuthContext from '../context/auth-context' ;

 <AuthContext.Provider 
          value= {{
            authenticated: this.state.authenticated, 
            login: this.loginHandler}}>
              
          {this.state.showCockpit? <Cockpit
            name = "SS"
            peopleLength = {this.state.people.length}
            showPersons = {this.state.showPerson}
            switch = {this.switchNameHandler}
            toggle = {this.togglePersonHandler}
            showCockpit = {this.state.showCockpit}
          /> : null}
          {persons}
        </AuthContext.Provider>

<AuthContext.Consumer> </AuthContext.Consumer>也與之類似恐似,用它包裹相關(guān)的代碼段杜跷,且包裝為一個(gè)函數(shù)。由context傳入所需要的數(shù)值。

return(
        <div className={styles.Cockpit}>
           ... Other content
            <AuthContext.Consumer>
                {(context) => <button onClick={context.login}>Login</button>}
            </AuthContext.Consumer>
            
        </div>
    );



Another Way to use <AuthContext.Consumer>. In this way, we can access the context in life cycle hooks like componentDidMount() exected after render.

In class-base components:

import AuthContext from '../../../context/auth-context';

class Person extends Component{
  static contextType = AuthContext;

    componentDidMount() {
        console.log(this.context.authenticated);
    }
}

render(){    
        return (
            <Aux>
                {this.context.authenticated ? <p>Authenticated!</p> : <p>Please Login !</p>}
                 ... Other content
            </Aux>
        )
    }

contextType is a reserved word, you have to write exactly the same.

In functional components, you can use useContext hook to achieve the same effect.

import AuthContext from '../../context/auth-context'

const Cockpit = (props) => {
  const authContext = useContext(AuthContext);
  console.log(authContext.authenticated);

   return(
        <div className={styles.Cockpit}>
           <button onClick={authContext.login}>Login</button>            
        </div>
    );
}


4.Http Request in React

When you send requests to a server, you won't get HTML pages back, instead, you will get JSON data.
Normally, your server is a RESTFUL API, just exposing some API endpoints to which you can send requests.

  • AJAX

  • axios

interceptors: that Axios calls for every request. You can use interceptors to transform the request before Axios sends it, or transform the response before Axios returns the response to your code.
It is useful for setting some common headers like authorization headers or handling errors globally.

// In index.js
axios.interceptors.request.use(request => {
    console.log(request);
    // always need to return the request, otherwise it blocks the request.
    // you can edit the request before you return
    return request;
}, error => {
    console.log(error);
    return Promise.reject(error);
});

axios.interceptors.response.use(response => {
    console.log(response);
    // Edit request config
    return response;
}, error => {
    console.log(error);
    return Promise.reject(error);
})

Removing Interceptors (otherwise could cause leak memory):

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
  • fetch()



5. Routing

The basic idea is to load different component codes for different paths.


Routing
  • Implement:
    install react-router-dom, then wrap the component, where you are going to use Router.
import { BrowserRouter } from 'react-router-dom';

class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <div className="App">
          <Blog />
        </div>
      </BrowserRouter>
    );
  }
}


  • Use <Route> component.
    import {Route, Link} from 'react-router-dom';

It can be configured with several attributes.

1.path: start with the certain path
2.exact: exactly the same path
3.render: render some JSX code under this path
4.component: render a component under this path

  <Route path="/" exact component={Posts}/>
  <Route path="/new-post" exact component={NewPost}/>


  • Use <Link></Link> instead of <a/> to avoid Application reload. to attribute in <Link/> as href in <a/>
    Create a router:
<header className="Blog">
                    <nav>
                        <ul>
                            <li><Link to="/">Home</Link></li>
                            <li><Link to="/new-post">New Post</Link></li>
                        </ul>
                    </nav>
                </header>

You can also pass an Object to the to attribute in <Link></Link>

<li><Link to = {{
  pathname = '/new-post',
  hash: 'submit',
  search: '?quick-submit=true'
}}>New Post</Link></li>
  1. pathname: relate to the path you set in <Route/>. It is always treated as an absolute path.
  2. hash: Used to jump to a certain part of your page, or scroll smoothly there.
  3. search:
    ...


  • <NavLink>
    可以使用<NavLink>添加activeStyle attribute 來給Route添加樣式葱椭。如果不需要樣式,使用<Link></Link>即可口四。
<li><NavLink
  to="/"
  exact
   activeClassName = "my-active"
  activeStyle = {{
              color: '#fa923f',
              textDecoration: 'underline'
  }}
>Home</NavLink></li>



整體代碼:

import React, { Component } from 'react';
import {Route, Link} from 'react-router-dom';
import Posts from './Posts/Posts';
import NewPost from './NewPost/NewPost';
import './Blog.css';

class Blog extends Component {

    render () {
        return (
            <div>
                <header className="Blog">
                    <nav>
                        <ul>
                            <li><Link to="/">Home</Link></li>
                            <li><Link to="/new-post">New Post</Link></li>
                        </ul>
                    </nav>
                </header>
                {/* <Route path="/" exact render={() => <h1>Home</h1>}/> */}

                {/* loading components */}
                <Route path="/" exact component={Posts}/>
                <Route path="/new-post" exact component={NewPost}/>
            
                
            </div>
        );
    }
}

export default Blog;

運(yùn)行效果:



  • withRouter High Order Component
    We use the HOC to wrap the export to make the exported component route aware. And it will get the props containing information for the nearest loaded route.
import {withRouter} from 'react-router-dom';


  • Absolute Path vs Relative Path
    An absolute path is always appended to your domain. 在<Link>的 to attribute 中孵运, 所有的path都默認(rèn)為Absolute path.

    Sometimes, you might want to create a relative path instead. This is especially useful if your component is already loaded given a specific path (e.g. posts ) and you then want to append something to that existing path (so that you, for example, get /posts/new ).

    If you're on a component loaded via /posts , to="new" would lead to example.com/new , NOT example.com/posts/new.

    使用以下方法可以獲得dynamic path:

<Link to={props.match.url + '/new'}>


  • Route Parameters
    傳參(Pass Route Parameters):
    <Link to={'/' + this.props.id}></Link>
    提取參數(shù)(extract route parameters):
    <Route path ="/:id" exact component={FullPost}/>

But how do you extract search (also referred to as "query") parameters (=> ?something=somevalue at the end of the URL)? How do you extract the fragment (=> #something at the end of the URL)?

Query Params:
You can pass them easily like this:

<Link to="/my-path?start=5">Go to Start</Link>

or

<Link 
    to={{
        pathname: '/my-path',
        search: '?start=5'
    }}
    >Go to Start</Link>

React router makes it easy to get access to the search string: props.location.search.

But that will only give you something like ?start=5

You probably want to get the key-value pair, without the ? and the = . Here's a snippet which allows you to easily extract that information:

componentDidMount() {
    const query = new URLSearchParams(this.props.location.search);
    for (let param of query.entries()) {
        console.log(param); // yields ['start', '5']
    }
}

URLSearchParams is a built-in object, shipping with vanilla JavaScript. It returns an object, which exposes the entries() method. entries() returns an Iterator - basically, a construct which can be used in a for...of... loop (as shown above).

When looping through query.entries(), you get arrays where the first element is the key name (e.g. start ) and the second element is the assigned value (e.g. 5).

Fragment:
You can pass it easily like this:

<Link to="/my-path#start-position">Go to Start</Link>

or

<Link 
    to={{
        pathname: '/my-path',
        hash: 'start-position'
    }}
    >Go to Start</Link>

React router makes it easy to extract the fragment. You can simply access props.location.hash.


  • Using Switch
    Switch tells React to only load the first Route that matches from a given set of routes.
 <Switch>
          <Route path="/" exact component={Posts}/>
           <Route path="/new-post" exact component={NewPost}/>
           <Route path ="/:id" exact component={FullPost}/>
</Switch>

When we wrap our routes within Switch, and the first route that matches a given path will be loaded. After, it will just stop analyzing the routes, it won't render any other route.


  • Nested Route
    It is a condition that you implement a Route inside a component rendered by a Route.
<Route path ={this.props.match.url + "/:id"} exact component={FullPost}/>

Use this.props.match.url dynamically get parent url.


  • Conditional Redirects
  1. Use <Redirect/> to redirect.
    Use it as a component. Render it to leave the current page.
import {Redirect} from 'react-router-dom';
<Redirect to="/posts"/>
  1. Use this.props.history.push("path") or this.props.history.replace("path") to redirect.
    Difference: push() function push a new page on the satck, whereas redirect and replace() function replace the current page.


  • Navigation Guard
    It is usually used when you don't know whether the user is authenticated or not. Or in your application, there are some routes that you only allow authenticated users to access.


  • Handle unknown routes 404
 <Switch>
          <Route path="/" exact component={Posts}/>
           <Route path="/new-post" exact component={NewPost}/>
           <Route path ="/:id" exact component={FullPost}/>
           <Route render = {() => <h1>Not Found</h1>}/>
</Switch>


  • Lazy Loading

Only loading the component once you need it.

Now the thing is whenever you are importing something with import from somewhere.
you basically inform webpack, the build tool which gets used behind the scenes about this dependency and it will include it in the global bundle, this is its job.

Now for the lazy loading, this is exactly the opposite of what we want to do, we don't want to include it in the bundle, we want to load it when needed.

Still, webpack needs to be able to dynamically prepare some extra bundle for this potentially loaded code.

  1. Create a Higher-Order Component:
import React, {Component} from 'react';

const anycComponent = (importComponent) => {
    return class extends Component{
        state = {
            component: null
        }

        componentDidMount(){
            importComponent()
            .then(cmp => {
                this.setState({component: cmp.default});
            });
        }

        render () {
            const C = this.state.component;

            return C ? <C {...this.props}/> : null;
        }
    }
}

export default anycComponent;
  1. import the component we want to dynamically load in this HOC.
import React, { Component } from 'react';
import {Route, NavLink, Switch} from 'react-router-dom';
import Posts from './Posts/Posts';
//import NewPost from './NewPost/NewPost';
import asyncComponent from '../../hoc/asyncComponent';

const AsyncNewPost = asyncComponent(() => {
    return import('./NewPost/NewPost');
})

class Blog extends Component {
    render () {
        <div>
            <Switch>
                    <Route path="/new-post" exact component={AsyncNewPost}/>
                    <Route path="/posts" component={Posts}/>
            </Switch>
        </div>   
    }
}
export default Blog;



React 16.0 版本及以上,可用React.lazy()方法引入component蔓彩,并使用<suspense></suspense> component 將其包裹:

import React, { Component, Suspense } from 'react';
import { BrowserRouter, Route, NavLink } from 'react-router-dom';

import User from './containers/User';
import Welcome from './containers/Welcome';

const Posts = React.lazy(() => import('./containers/Posts'));

class App extends Component {
  state = { showPosts: false };

  modeHandler = () => {
    this.setState(prevState => {
      return { showPosts: !prevState.showPosts };
    });
  };

  render() {
    return (
      <React.Fragment>
        <button onClick={this.modeHandler}>Toggle Mode</button>
        {this.state.showPosts ? (
          <Suspense fallback={<div>Loading...</div>}>
            <Posts />
          </Suspense>
        ) : (
          <User />
        )}
    );
  }
}

export default App;


6. Redux

It is a library often used in React projects to manage application state.

Redux work flow

There are four stages to building a Redux app:

1.Visualise the state tree
2.Design your reducers
3.Implement Actions
4.Implement Presentation
























最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末治笨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赤嚼,更是在濱河造成了極大的恐慌旷赖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件更卒,死亡現(xiàn)場離奇詭異等孵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹂空,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門俯萌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人上枕,你說我怎么就攤上這事咐熙。” “怎么了辨萍?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵棋恼,是天一觀的道長。 經(jīng)常有香客問我锈玉,道長爪飘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任嘲玫,我火速辦了婚禮悦施,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘去团。我一直安慰自己抡诞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布土陪。 她就那樣靜靜地躺著昼汗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鬼雀。 梳的紋絲不亂的頭發(fā)上顷窒,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼鞋吉。 笑死鸦做,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谓着。 我是一名探鬼主播泼诱,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赊锚!你這毒婦竟也來了治筒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤舷蒲,失蹤者是張志新(化名)和其女友劉穎耸袜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牲平,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堤框,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纵柿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胰锌。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖藐窄,靈堂內(nèi)的尸體忽然破棺而出资昧,到底是詐尸還是另有隱情,我是刑警寧澤荆忍,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布格带,位于F島的核電站,受9級特大地震影響刹枉,放射性物質(zhì)發(fā)生泄漏叽唱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一微宝、第九天 我趴在偏房一處隱蔽的房頂上張望棺亭。 院中可真熱鬧,春花似錦蟋软、人聲如沸镶摘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凄敢。三九已至,卻和暖如春湿痢,著一層夾襖步出監(jiān)牢的瞬間涝缝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拒逮,地道東北人罐氨。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像滩援,于是被迫代替她去往敵國和親岂昭。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容