React学习笔记(下)

内容概要:

  • 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时赋值为当前element,在unmounted时重新变为nullref的值在 componentDidMountcomponentDidUpdate之前更新

useRef()
在默认情况下,functional component 中不能使用ref。 然而可以使用 useRef() React Hook 在functional component 中创建并初始化 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完成后,被默认点击一次。


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() 可以创建并初始化context的值,这个值可以是一个 JavaScript Object, 也可以是 number, array, string等等。你可以自定义这个值的有效范围, 让他在React Components中传递。

  • Provider & Consumer
    在App.js中,将需要传递的值赋给在上例中已提前定义好的JS对象。使用<AuthContext.Provider></AuthContext.Provider> 包裹你需要使用此value的内容。
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>也与之类似,用它包裹相关的代码段,且包装为一个函数。由context传入所需要的数值。

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;

运行效果:



  • 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都默认为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>
    提取参数(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
























最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容