內(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
的值在 componentDidMount
或 componentDidUpdate
之前更新
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.
-
Implement:
installreact-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/> ashref
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>
- pathname: relate to the path you set in <Route/>. It is always treated as an absolute path.
- hash: Used to jump to a certain part of your page, or scroll smoothly there.
- 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 toexample.com/new
, NOTexample.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
- 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"/>
- Use
this.props.history.push("path")
orthis.props.history.replace("path")
to redirect.
Difference:push()
function push a new page on the satck, whereasredirect
andreplace()
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.
- 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;
- 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.
There are four stages to building a Redux app:
1.Visualise the state tree
2.Design your reducers
3.Implement Actions
4.Implement Presentation