*【需要升入理解】React deeper into Compo
这里涉及很多去优化app的内容,在实践不足的情况下理解会比较局限,最好可以在反复实践过程中回头理解和尝试优化自己的程序。
A better project structure
- components
- contsainers
- assets
- splitting an app into components
import React from 'react'
import Person
const persons(props)=>props.persons.map((person,index)=>{
return<Person
click = {()=>this.clicked(index)}
name = {person.name}
age = {person.age}
key = {person.id}
changed={(event)=>this.changed(event,person)}
/>
})
...
import Persons
<Persons
persons = {this.state.persons}
clicked = {this.deletePersonHandler}
changed = {this.nameChangedHandler}/>
stateless component VS stateful component
stateless component :functional component without state hoc
stateful component: class component & functional component with state hoc
stateless:presentational componet
=>
Class based component VS functional Component
Jietu20200523-211717.jpgfunctional component =>props.xxx不需要this
Class component Lifecycle
constructor()
getDerivedStateFromProps()?
getSnapshotBeforeUpdate()?
componentDidCatch()
componentWillUnmount().
shouldComponentUpdate()?
componentDidUpdate()
componentDidMount()
render()
Component Lifecycle--Creation
- ‘Lifecycle Hooks’ have nothing to do with ‘React Hooks’
- (ES6 feature=>)constructor(props)=>[1.Call super(props); 2.Do:set state; 3.Don’t set side-effect(http requests/store sth in localStorage in browsers);(ES7 do not need)]
- getDerivedStateFromProps(props,state)=>[1.Do: Sync state; 2.Don’t set side-effect]
- render()=>[prepare & Structure ur JSX Code]
- Render Child Components
- ComponentDidMount()[1.Do: Side-effect; 2.Don’t update state]
-
ComponentWillMount()
static getDerivedSTateFromProps(props,state){}
Component Lifecycle--Update
- getDerivedStateFromProps(props,state)=>[1.Do: Sync state; 2.Don’t cause side-effect]
- (may cancel updating process=>)shouldComponentUpdate(nextProps,nextState)=>[1.Do:decide whether to contiue or not;2.Dont’t:Cause side-effect]
- render()=>[prepare & Structure ur JSX Code]
- Update Child Components
- getSnapshotBeforeUpdate(prevProps,prevState)[1.Last-minute DOM ops;2.Dont’t:Cause side-effect]
-
ComponentDidUpdate()[1.Do: Cause Side-effect; 2.Don’t update state]
shouldComponentUpdate(nextProps,nextState)
return true or false, when false stop update
using useEffect()
in functional components
1.useEffect(()=>{...//Http requests}) 当render该组件时会跑一遍(其他组件rerender带动该组件render也会跑一遍
2.useEffect(()=>{...//Http requests},[props.persons]) 当persons改变时就会跑一遍
3.useEffect(()=>{...//Http requests},[]) 任何props改变都会跑一遍
import React,{useEffect} from 'react'
const cockpit = (props)=>{
useEffect(()=>{
//Http requests
},[props.persons])//当persons改变时就会跑一遍
}
Cleaning up with Lifecycle Hooks & useEffect() 组件有显示状态和不显示状态,不显示时其中的一些方法就需要‘clean up’[lifecycle hooks 和react hooks无关]
在useEffect里加return一个arrow function
- it runs before the main useEffect function
- after the first render cycle
- 只有在组件被‘clean up’才会run
const cockpit = props =>{
useEffect(()=>{
//...
const timer = setTimeout(()=>{
alert('Saved data to cloud');
},1000);
return()=>{
clearTimeout(timer);
console.log([cockpit.js] cleanup work in useEffect');
}
},[])
}
优化 Using shouldComponentUpdate() for Optimization
- 父组件包子组件,当父组件state变化默认会render他的所有内部组件,
这时候某些子组件并不需要跟新,因为与他相关的值并没有发生改变
shouldComponentUpdate(nextProps,nextState){
if(nextProps.persons !== this.props.persons){
return true;//update(rerendering)
}else{
return flase;
}
}
Note
componentDidUpdate()
will not be invoked if shouldComponentUpdate()
returns false.
this.props
与nextProps
和进行比较this.state
,nextState
然后返回false
以告知React可以跳过更新。请注意,返回false
并不能防止子组件在其状态更改时重新呈现。
目前,如果shouldComponentUpdate()
回报false
,然后UNSAFE_componentWillUpdate()
,render()
和componentDidUpdate()
将不会被调用。将来,React可能会被shouldComponentUpdate()
视为提示而不是严格的指令,并且返回false
可能仍会导致组件的重新渲染。
Optimizing Fuctional Components with React.memo() [componentDidUpdate() in functional component]
- 解释: 父组件包子组件,当父组件state变化默认会render他的所有内部组件,
这时候某些子组件并不需要跟新,因为与他相关的值并没有发生改变,比如一个function component他只用到一个props中一个attr,所以希望通过一种方式减少不必要的render - 解释:
比如一个function component他只用到一个props中一个attr,但是props每个属性改变都会造成他的rerender,所以希望通过一种方式减少不必要的render - 使用注意:传props给他时只传相关值,比如我要用this.state.persons.age, 那在传递参数时
就只传递Dothis.state.persons.age,而不是Don’tthis.state.persons
export default React.memo(cockpit);
PureComponents instead of shouldComponentUpdate()
//只有当相关变量update才render
shouldComponentUpdate(nextProps,nextState){
if(
nextProps.persons !== this.props.persons ||
nextProps.changed !== this.props.changed ||
nextProps.clicked !== this.props.clicked
){
return true;
} else {
return false;
}
}
替代
实现和前面 shouldComponentUpdate()想做的目标=》和子组件无关的即使父组件rerender也不会直接导致子组件rerender
import React,{PureComponent} from 'react';
class Persons extends PureComponent{
}
How React Updates The Real DOM
Jietu20200525-163011.jpgRendering Adjacent JSX Elements
1.<></>
2.[<p key=‘i1’/>,<div key=‘i2’/>]
3.Aux:(higher order component-hoc)
4.React.Fragment<React.Fragment></React.Fragment>
//Aux.js
import React from ' react';
export default const aux = props=>props.chilren;
<Aux>
...
</Aux>
...解释
JSX:
import React from 'react';
const heading = props => (
<h1>{props.title}</h1>
<h2>{props.subtitle}</h2>
);
export default heading;
This is NOT allowed because it would be translated to:
import React from 'react';
const heading = props => React.createElement('h1', {},
props.title) React.createElement('h2', {}, props.subtitle);
export default heading;
This is invalid JavaScript syntax, you’re trying to return two expressions (two React.createElement() calls).
You are allowed to do that if you
a) return an array of React.createElement() calls OR
b) return a single React.createElement() call that wraps the other two
a)
import React from 'react';
const heading = props => [
React.createElement('h1', {key: 'i1'}, props.title),
React.createElement('h2', {key: 'i2'}, props.subtitle)
];
export default heading;
This is equivalent to returning an array of keyed JSX elements.
b)
import React from 'react';
import Aux from '../hoc/Aux';
const heading = props => React.createElement(
Aux,
{},
React.createElement('h1', {key: 'i1'}, props.title),
React.createElement('h2', {key: 'i2'}, props.subtitle)
);
export default heading;
This is equivalent to using <Aux>.
b) works because we can pass as many children (third argument to React.createElement()) as we want.
Higher Order Components(HOC) intro
- intro
//WithClass.js
import React from 'react';
export default const withClass = props=>(
<div className={props.classes}>{props.children}</div>
)
//App.js
...
return(
<WithClass classes={classes.App}>
...
</WithClass>
)
- Another Form of Hocs
对于function 的js
- 小写开头.js=>function
- 大写开头.js=>functional component
//withClass.js
import React from 'react';
export default const withClass = (WrappedComponent, className)=>(
return props=>(<div className={className}><WrappedComponent/></div>)
)
//App.js
...
return(
...
)
export default withClass(App,classes.App);
Passing unkonwn props
//withClass.js
import React from 'react';
export default const withClass = (WrappedComponent, className)=>(
return props=>(<div className={className}>
<WrappedComponent {...props}/>
</div>)
)
Setting state correctly
- 在setState中不要引用old state来更新new state
Using Prop Types
npm install --save prop-types
import PropTypes from 'prp-types';
class Person extends Component{
......
}
Person.propTypes = {
click: PropTypes.func,
name:PropTypes.string,
age:PropTypes.number,
changed: PropTypes.func
}
Using Refs
componentDidMount(){
this.inputElement.focus();
}
...
<input
ref={(inputEl)=>{this.inputElement = inputEl}}/>
constructor(props){
super(props);
this.inputElement.current.focus();
}
...
<input
ref={this.inputElement}/>
Refs With React Hooks
import React,{useRef,useEffect} from 'react'
const cockpit = props =>{
const toogleBtnRef = useRef(null);
useEffect(){
//理解运行周期(顺序),functional component Ref只能在useEffect中进行操作(因为useEffect是在return后run的)
toogleBtnRef.current.focus();
}
...
return(
<button ref={toogleBtnRef}/>
)
}
Understanding Prop Chain Problems
Using the Context API
//auth-context.js
import React from 'react';
const authContext = React.createContext({authenticated: false,login()=>{}});
export default authContext;
...
//App.js
import AuthContext from '...'
<AuthContext.Provider value={authenticated:this.state.authenticated,login:this.loginHandler}>
<Cockpit .../>//不用再传login方法
{person}
</AuthContext.Provider>
//person.js
import AuthContext from '...'
return(
<AuthContext.Consumer >
{(context)=>{
context.authenticated?<p>true</p>:<p>false</p>
}}
</AuthContext.Consumer>
)
//cockpit.js
return(
<AuthContext.Consumer >
{(context)=>{
<button onClick={context.login}>Log in</button>
}}
</AuthContext.Consumer>
)
contextType & useContext()
- contextType class based
import AuthContext from '...'
static contexType = Authontext;
componentDidMount(){
var a = this.context.Authontext
}
return(...{this.context.Authontext})
- useContext() function based
in functional component
import AuthContext from '...'
import {useContext} from 'react'
...
const authContext = useContext(AuthContext);
补充
Static properties are properties of a class, not of an instance of a class.
https://medium.com/front-end-weekly/understanding-static-in-javascript-10782149993
Useful Resources & Links:
-
More on useEffect(): https://reactjs.org/docs/hooks-effect.html
-
State & Lifecycle: https://reactjs.org/docs/state-and-lifecycle.html
-
PropTypes: https://reactjs.org/docs/typechecking-with-proptypes.html
-
Higher Order Components: https://reactjs.org/docs/higher-order-components.html