函数组件
接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素的组件被称为函数组件
注意点:
1. 函数组件里没有this和生命周期,不能使用string ref!!!(因为不存在this,无法使用this.refs)
2. 使用函数式组件时,应该尽量减少在函数中声明子函数,否则,组件每次更新时都会重新创建这个函数
(可使用useMemo来解决)
3. 不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。
4. 不要在普通的 JavaScript 函数中调用 Hook, 只在 React 函数中调用 Hook。
Hook(钩子函数)
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
只有在函数式组件中能使用hook,在类式组件中不能使用hook?
常用hook
- useState
import {useState} from 'react';
function App() {
const [state, setState] = useState({
name: 'xiaobai',
age: 20
})
const changAge = function(){
setState({
name:state.name,//因为浅合并的特性,改变age同时也需要把其他属性带上!!!
age:state.age+1
})
}
return <div>
<span>name:{state.name}</span>
<br></br>
<span>name:{state.age}</span>
<button onClick={changAge}>点击改变年龄</button>
</div>;
}
export default App;
另一点需要注意的是,useState 多次调用set方法不会自动合并!!!
-
useEffect
该 Hook 接收一个包含命令式、且可能有副作用代码的函数
副作用包括改变 DOM、添加订阅、设置定时器、记录日志等。
-
调用形式:
useEffect(()=>{ //副作用函数 return ()=>{ //返还函数 }; },[依赖参数])
-
useEffect的执行过程大概是这样的:
1. 挂载阶段: 从上往下执行代码,执行的过程中,遇到useEffect,则将副作用函数推入一个专属队列。 在组件挂载完成之后,会将所有的副作用函数执行,同时将所有的返还函数推入另一个专属队列。 即:挂载->副作用函数 2. 更新阶段: 从上往下执行代码,执行的过程中,遇到useEffect,则将副作用函数推入一个专属队列。 在组件挂载完成之后,先看返还函数专属队列是否为空,若不为空,则先执行所有的返还函数。 之后再执行所有的副作用函数,同时将所有的返还函数推入另一个专属队列。 即:挂载->返还函数->副作用函数 3. 卸载阶段: 执行返还函数队列中的所有的返还函数 即:返还函数 总的来说,useEffect可以基本上看成是componentDidMount,componentDidUpdate,componentWillUnmount的结合
-
依赖参数的不同取值的影响
依赖参数:组件更新时,副作用和返还函数执行前会判断依赖参数是否更新。若更新,则执行;否则,不执行。 1. [依赖参数]为空 相当于依赖所有的参数,组件挂载、更新都会执行副作用函数。 2. [依赖函数]为[] 不依赖任何参数,只执行挂载阶段。 3. 其他 依赖给定参数数组中的所有参数,组件挂载或参数改变时会执行副作用函数。
- useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
可以用来取得组件更新前的一些数据!!!
const [nub,setNub] = useState(0);
const prevNub = useRef(nub);
useEffect(()=>{
console.log(nub,prevNub.current);//preNub.current可以用来记录nub上一次的值
prevNub.current = nub;// 使用 useRef 记录数据时,该数据不会随着组件更新而自动更新
},[nub])
return <div>
<span>{nub}</span>
<button onClick={()=>{
setNub(nub+1);
}}>nub递增</button>
</div>
- useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useMemo返回memorized值。把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
也就是说只有当a,b改变时,才会执行() => computeExpensiveValue(a, b);其他值的改变都不会执行!(类似vue中的computed)
自定义hook
自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。
与 React 组件不同的是,自定义 Hook 不需要具有特殊的标识。
每次调用 Hook,它都会获取独立的 state。
用自定义的hook来模拟实现sticky:
import { useEffect, useState } from "react";
function useScroll() {
const [y,setY] = useState(window.scrollY);
useEffect(()=>{
window.onscroll = function() {
setY(window.scrollY);
}
return ()=>{
window.onscroll = null;
}
},[])
return [y,(newY)=>{
window.scrollTo(0,newY);
setY(newY);
}];
}
export {useScroll};
测试效果所用代码:
import { useEffect, useRef } from "react";
import { useScroll } from "./useScroll.js";
import './index.css';
function App() {
const [scrollY, setScrollY] = useScroll();
let buttonEl = useRef();
useEffect(() => {
if (scrollY > window.innerHeight) {
buttonEl.current.style.display = "block";
} else {
buttonEl.current.style.display = "none";
}
}, [scrollY])
return <div className="test">
<button ref={buttonEl} onClick={()=>{setScrollY(0)}}>{scrollY}</button>
</div>
}
export default App;
css:
.test{
height:4000px;
background-color: bisque;
}
button{
position: fixed;
bottom: 0;
}
memo
1.调用 memo 方法,要传入一个组件,memo 会返回一个新的组件(高阶组件)
2.调用 memo 返回的组件时,memo 内部会调用我们传入的组件
3.在 memo 父组件更新时,memo 会进行 prevProps 和 当前的 props 的浅对比,如果对比结果为 true 则不更新 子组件
简单使用举例:(data时传入child的数据,这样一个child更新不会导致其他child更新)
export default memo(Child,(props1,props2)=>{
return props1.data === props2.data;
});
React Router
React Router提供了多种环境下的路由库:web、native
基于web的React Router为react-router-dom
基于native的React Router为react-router-native(www.npmjs.com/package/rea…)
( react-router、react-router-dom、react-router-native 关系: www.cnblogs.com/cag2050/p/9… )
react-router-dom
- 下载:
npm i -S react-router-dom
-
BrowserRouter组件和HashRouter组件
BrowserRouter组件--基于HTML5 History API的路由组件(不带#号)
HashRouter组件--基于URL Hash的路由组件(带#号)
index.js
ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById('root') };
-
route路由组件
-
path
-
path默认模糊匹配,匹配开头,如path='/',则能匹配到以'/'开头的所有url,如'/index'等;
-
使用extract表示路由使用 精确匹配模式,如path='/about',加上extract只能匹配到'/about'和'/about/';
-
使用strict表示路由使用 严格匹配模式,如path='/about',加上strict只能匹配到'/about';注意,strict是基于extract的,如果没有extract, strict不起效果!!!
-
多路径匹配:数组 -> 如path={["/","/home","/index"]}
-
path为空,则直接匹配成功
-
动态路由:如/list/:type?/:page?
-
-
Redirect组件:重定向
to = 将 url 重新定义的结果 from = 要进行重定向的 url,如果不写代表所有地址都进行重定向
-
component
component是要显示的组件,使用component不能传递props!
<Route path="/about/details" exact strict component={AboutDetailsPage} />
-
render
使用render能向组件中传递参数!
<Route path="/about/details" exact strict render={(routerProps)=>AboutDetailsPage} />
路由参数(上面的routerProps):被 Route 调用的组件称之为路由组件, Route 在调用该组件时,会将路由相关的信息通过参数的形式,传递给组件。
history:历史记录信息 length -(数字)历史记录堆栈中的条目数 action - (字符串)当前动作(PUSH,REPLACE,或POP) push(path, [state]) -(功能)将新条目推入历史记录堆栈 replace(path, [state]) -(函数)替换历史记录堆栈上的当前条目 go(n)-(函数)通过n条目在历史记录堆栈中移动指针 goBack() -(功能)等同于 go(-1) goForward() -(功能)等同于 go(1) location pathname - 当前的url search - 当前 url 中 seach hash - 当前 url 中hash state - replace 或 push 传递的信息 match params - 匹配到的动态路由的值 isExact - 是否可以精确匹配(不是指是否设置了extract!) path -(字符串)当前 Route 对应的 path url - URL中的被匹配到的部分
-
-
Link组件
<Link to="/">首页</Link>
-
NavLink组件
<Nav to="/" activeClassName="selected" activeStyle={{fontWeight:"bold"}} isActive={()=>{}}>首页</Nav>
- NavLink功能与Link一致,但多了当前选中项
- 使用NavLink组件时,NavLink会根据当前url及自身的to属性去做匹配,匹配成功则给当前项加上选中的class
- activeClassName:匹配成功之后,要添加的class名,默认为 active
- activeStyle:匹配成功之后,要显示的style
- isActive() 判断当前是否应该选中, 返回值 true 选中,false 不选中
-
Switch组件
其中一个 Route 匹配成功的话则不继续匹配 (不使用Switch的话Route匹配成功的话还会继续匹配)
-
其他:非路由组件如何获取路由参数
- 使用高阶组件 -- withRouter(高阶路由)
- hooks -- useHistory、useLocation、useParams、useRouteMatch
-
其他:图片显示问题
- 使用import
import {img} from '../imgs/img.png' <img src={img}/>
- 使用require
<img src={require('../imgs/img.png').default}/>
-
示例
const types = ["good","share","ask"];
function App() {
const user = "小明";
return (
<div>
<Nav />
<Switch>
<Route path={["/","/home","/index"]} exact render={(routerProps)=>{
return <IndexPage user={user} {...routerProps} />
}} />
<Route path="/about" component={AboutPage} />
<Route path="/join" exact component={JoinPage} />
<Route path="/list/:type?/:page?" exact render={(routerProps)=>{
const {params} = routerProps.match;
const {type="good",page="1"} = params;
if(types.includes(type)
&& parseInt(page) + "" === page
&& page > 0){
return <ListPage />
}
return <Redirect to="/404" />;
}} />
<Route path="/404" exact component={UndefinedPage} />
{/* 其他:跳转到404 */}
<Redirect to="/404" />
</Switch>
</div>
);
}
Nav组件:
function Nav() {
return <nav className="nav">
<NavLink
to="/"
activeClassName="selected"
activeStyle={{
fontWeight: "bold",
textDecoration: "underline"
}}
isActive={(match,location)=>{
const {pathname} = location;
return pathname==="/"||pathname==="/index"||pathname==="/home";
}}
>首页</NavLink>
<span> | </span>
<NavLink
to="/about"
exact
activeClassName="selected"
activeStyle={{
fontWeight: "bold",
textDecoration: "underline"
}}
>关于</NavLink>
<span> | </span>
<NavLink
to="/join"
exact
activeClassName="selected"
activeStyle={{
fontWeight: "bold",
textDecoration: "underline"
}}
>加入</NavLink>
<span> | </span>
<NavLink
to="/list"
activeClassName="selected"
activeStyle={{
fontWeight: "bold",
textDecoration: "underline"
}}
>列表</NavLink>
<hr />
</nav>
}