react入门

吴龙淼 写于 2021-10-11

react 项目构建

webpack npx create-react-app my-app vite yarn create @vitejs/app my-app npm init @vitejs/app my-vue-app

react

state

惰性初始化state
const [initState,setinitState]=useState(()=>{return initData})
// 获取最新state
setinitState((nowState)=>{return nowState+1})

react18之前
在事件处理函数内部的 setState 是异步的;promise.settimeout,原生事件中setState在内是同步的

react18
默认所有 setState 是异步的。

state的异步是按批处理状态,不是真正的异步

强制立即更新状态,函数内部的多个 setState 仍然为批量更新
import { flushSync } from 'react-dom';
flushSync(() => {
  setCount(count + 1);
});

props==vue prop

this.prop.ParentAttributes

hook

必须在最外层函数(组件)使用

state hook
const [state, setState] =useState("初始值")

effect hook
useEffect(){

}

自定义hook
useMyEffect(){
return [state,setState]
}

context

发送
export const MyContext = React.createContext(defaultValue);
<MyContext.Provider value={/* 某个值 */}>
接收
import { MyContext } from "../index";
MyContext.Consumer._currentValue;


发送
import {useContext,createContext} from 'react'
const MyContext = createContext(defaultValue);
<MyContext.Provider value={/* 某个值 */}>
</MyContext.Provider>
接收
useContext(MyContext)

refs

在组件重新渲染后,不会改变,设置current不会引起重新渲染

// 传递refs
const myRef = useRef(null)
const node = myRef.current;
  render() {
    return <div ref={myRef} />;
  }



获取未来帧的值
function Example() {
    const [count, setCount] = useState(0);

    const currentCount = useRef(count);

    currentCount.current = count;

    const handleClick = () => {
      // 延迟3秒,取得未来帧的count
        setTimeout(() => {
            setCount(currentCount.current + 1);
        }, 3000);
    };

    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>
                setCount
            </button>
            <button onClick={handleClick}>
                Delay setCount
            </button>
        </div>
    );
}


获取过去帧的值
function Example4() {
    const [count, setCount] = useState(1);

    const prevCountRef = useRef(1);
    // 保存前一个count
    const prevCount = prevCountRef.current;
    // 设置最新count
    prevCountRef.current = count;

    const handleClick = () => {
        setCount(prevCount + count);
    };

    return (
        <div>
            <p>{count}</p>
            <button onClick={handleClick}>SetCount</button>
        </div>
    );
}

hooks闭包陷阱

使用hooks,dps数组未正确使用

// 组件完全渲染后执行,==vue onMounted
useeffrct(()=>{
 return setinterval(fn,1000)
},[])


// 组件layout之后,patting之前,==vue onBeforeMounted
useLayoutEffect(()={},[])

React.memo

接受到的属性不变,则不重新渲染函数,减少组件渲染次数,需要封装成单独的组件

React.memo
memo()

useMemo

把创建函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算

useMemo(()=>({number}),[number]);

return []解构赋值当作useCallBack使用
const [a,b] = useMemo(()=>({return [a,b]}),[number]);

useCallback

接收一个内联回调函数参数和一个依赖项数组(子组件依赖父组件的状态,即子组件会使用到父组件的值) ,useCallback 会返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新

useCallback(()=>{setNumber(number+1);},[number]);

useReducer

useState底层实现,复杂state,有依赖关系适合使用

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {number: state.number + 1};
    case 'decrement':
      return {number: state.number - 1};
    default:
      throw new Error();
  }
}
function Counter(){
    const [state, dispatch] = useReducer(reducer,{number:0});
    return (
        <>
          Count: {state.number}
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
    )
}

lazy && Suspense

<Suspense fallback={<div>test</div>}>
  路由组件
</Suspense>

路由懒加载
lazy(()=>import('url'))

react-router-dom

npm install react-router-dom@6 --save-dev

基本使用

import { Route, Routes } from "react-router-dom";

<BrowserRouter>
<Routes>
  <Route path="/" element={<Home />}></Route>
  <Route path="/about" element={<About />}></Route>
</Routes>
</BrowserRouter>

嵌套路由

<Routes>
  <Route path="user" element={<Users />}>
    <Route path=":id" element={<UserDetail />} />
    <Route path="create" element={<NewUser />} />
  </Route>
</Routes>

import  { useRoutes } from 'react-router-dom'
function App() {
  const element = useRoutes([
    {
      path: '/',
      element: <Layout />,
      children: [
        {
          path: 'auth/*',
          element: <Auth/>
        },
        {
          path: 'basic/*',
          element: <Basic/>
        }
      ]
    }
  ])
  return (
     {element}
  )
}


访问 /user/123,渲染成
<Users>
    <UserDetail/>
</Users>

访问/user/create,渲染成
<Users>
    <NewUser/>
</Users>

重定向

<Route path="*" element={<Navigate to="/home" />} />

获取参数

params
Route path="/list/:id" component={List}
<Link to="/list/2" >跳转页面</Link>
this.props.history.push("/list/2");
//读取参数:this.props.match.params.id
let params = useParams();

search
<Route path='/web/departManange ' component={DepartManange}/>
<link to="web/departManange?tenantId=12121212">xxx</Link>
this.props.history.push({pathname:"/web/departManange?tenantId" + row.tenantId});
读取参数用: this.props.location.search
let [searchParams, setSearchParams] = useSearchParams();

state
<Route path='/sort ' component={Sort}/>
<Link to=>
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});
//读取参数: this.props.location.query.state;

query
<Route path='/query' component={Query}/>
<Link to=>
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});
读取参数用: this.props.location.query.name

编程式导航useNavigate

旧hooks useHistory
import { useHistory } from 'react-router-dom';
let history = useHistory();
history.push('/home','sada');
history.replace('/home',{test:'aaa'});

新hooks useNavigate
let navigate = useNavigate();
function handleClick() {
 navigate("/manageData",{state:{value:111}});
 navigate('/home', {replace: true});
}
<button onClick={() => navigate(-2)}>