useEffect
useReducer
多个state需要一起更新时,就应该考虑使用useReducer,使用useReducer能够提高应用的性能
使用useReducer可以减少useState的使用,减少VDOM的render次数,也便于统一管理多变量
比如登录页面,多个状态,登陆成功要设置好多state,登录失败也要设置好多 登录页面例子
您也可以延迟创建初始状态。为此,您可以将init函数作为第三个参数传递。初始状态将设置为init(initialArg)。第三个参数是函数,延时处理
useMomo和usecallback
使用usecallback
当父组件传递状态给子组件的时候,memo好像没什么效果,子组件还是执行了,这时候我们就要引入hooks的useCallback、useMemo这两个钩子了。
//子组件会有不必要渲染的例子
interface ChildProps {
name: string;
onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
console.log('子组件?')
return(
<>
<div>我是一个子组件,父级传过来的数据:{name}</div>
<button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
</>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Child组件');
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo name={name} onClick={(newName: string) => setName(newName)}/>
</>
)
}
在上面代码基础上,父级调用子级时,在onClick参数上加上useCallback,参数为[],则第一次初始化结束后,不在改变。
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/>
{/* useCallback((newName: string) => setName(newName),[]) */}
{/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数
</>
使用usememo
interface ChildProps {
name: { name: string; color: string };
onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
console.log('子组件?')
return(
<>
<div style={{ color: name.color }}>我是一个子组件,父级传过来的数据:{name.name}</div>
<button onClick={onClick.bind(null, '新的子组件name')}>改变name</button>
</>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Child组件');
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo
name={{ name, color: name.indexOf('name') !== -1 ? 'red' : 'green'}}
onClick={ useCallback((newName: string) => setName(newName), []) }
/>
</>
)
}
更新属性name为对象类型,这时子组件还是一样的执行了,在父组件更新其它状态的情况下,子组件的name对象属性会一直发生重新渲染改变,从而导致一直执行,这也是不必要的性能浪费。 解决这个问题,使用name参数使用useMemo,依赖于State.name数据的变化进行更新
<ChildMemo
//使用useMemo,返回一个和原本一样的对象,第二个参数是依赖性,当name发生改变的时候,才产生一个新的对象
name={
useMemo(()=>({
name,
color: name.indexOf('name') !== -1 ? 'red' : 'green'
}), [name])
}
onClick={ useCallback((newName: string) => setName(newName), []) }
{/* useCallback((newName: string) => setName(newName),[]) */}
{/* 这里使用了useCallback优化了传递给子组件的函数,只初始化一次这个函数,下次不产生新的函数
/>
总结
在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可。而在使用函数的情况,需要考虑有没有函数传递给子组件使用useCallback。而在值有所依赖的项,并且是对象和数组等值的时候而使用useMemo(当返回的是原始数据类型如字符串、数字、布尔值,就不要使用useMemo了)。不要盲目使用这些hooks
react.createcontext
引用不能使用函数组件,只能用类组件
必须先用组件引用下,要不获取到的是空对象
或者这种洗写法也行
函数组件都可以消费
最好的使用useContext来消费
import React, { useContext } from "react";
import ReactDOM from "react-dom";
const TestContext= React.createContext({});
const Navbar = () => {
const { username } = useContext(TestContext)
return (
<div className="navbar">
<p>{username}</p>
</div>
)
}
const Messages = () => {
const { username } = useContext(TestContext)
return (
<div className="messages">
<p>1 message for {username}</p>
</div>
)
}
function App() {
return (
<TestContext.Provider
value={{
username: 'superawesome',
}}
>
<div className="test">
<Navbar />
<Messages />
</div>
<TestContext.Provider/>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useImperativeHandle
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值,说简单点就是,子组件可以选择性的暴露给副组件一些方法,这样可以隐藏一些私有方法和属性,官方建议,useImperativeHandle应当与 forwardRef 一起使用,具体如何使用看下面例子
const kun = useRef()
const introduce = useCallback (() => {
console.log('i can sing, jump, rap, play basketball')
}, [])
useImperativeHandle(ref, () => ({
introduce: () => {
introduce()
}
}));
return (
<div ref={kun}> { props.count }</div>
)
}
const KunKun = forwardRef(Kun)
function App () {
const [ count, setCount ] = useState(0)
const kunRef = useRef(null)
const onClick = useCallback (() => {
setCount(count => count + 1)
kunRef.current.introduce()
}, [])
return (
<div>
点击次数: { count }
<KunKun ref={kunRef} count={count}></KunKun>
<button onClick={onClick}>点我</button>
</div>
)
}
ref
创建ref3种方式
声明
引用
调用
useRef
不仅可以引用还可以保存数据
受控组件
react.lazy和suspense
高阶组件
应用场景
增强props
1,当子组件每一个都要添加一个属性,不用修改外部组件,只需要修改子组件内部即可
渲染判断鉴权
生命周期劫持
反向继承
class ComponentChild extends React.Component {
constructor(props) {
super(props)
this.state = {
num: 2019
}
}
componentDidMount() {
console.log("child component Did Mount")
}
clickComponent() {
// this.setState({num:2})
}
render() {
return (
<div>{this.state.num}</div>
)
}
}
let iihoc = WrapComponet => class extends WrapComponet {
constructor(props) {
super(props)
this.state = {
num: 2000
}
}
componentDidMount() {
console.log('iihoc componentDidMount')
console.log(this)
this.clickComponent()
// this.setState((pre) => ({ num: pre.num + 1 }))
}
render() {
return (
<div>
<div onClick={this.clickComponent}>'iiHoc 点击'</div>
这样的方式比属性代理好的是,在外部组件可以调用被继承组件的方法。但不能将被继承的 state 和 钩子覆盖掉
{/* <div><WrapComponet /></div> */}
这样的方式,外部组件的 state 可以将,被继承组件的 state 和 钩子函数彻底覆盖掉。同时,外部组件也可以调用被继承组件的方法。
<div>{super.render()}</div>
</div>
)
}
}
export default iihoc(ComponentChild)
常用的高阶组件
connect,withrouter,forwardRef
直接在函数组件上加ref会报错,需要使用forwardRef包装