React创建项目,hook,router,redux

133 阅读5分钟
配置文件详细介绍,最新版本

react.png

前期准备

在开始创建项目前,确认是否安装了node.js运行环境,打开电脑终端输入node -v即可,若没有版本号。大家可自行下载。这里开始“简单”记录一下react通过脚手架创建项目的过程。

npm install -g create-react-app    全局安装create-react-app
npm uninstall -g create-react-app  卸载
create-react-app -V        检测React版本号
 

React项目创建

  1. 打开终端,输入命令:npx create-react-app react-project-demo --template typescript
  2. 等待项目创建完成,然后输入命令:cd react-project-demo
  3. 输入命令:npm start
  4. 打开浏览器,输入地址:http://localhost:3000/
npx create-react-app react-project-demo --template typescript
npm init vite@latest react-project-demo --template typescript

cd react-project-demo
npm start

安装路由

  1. 输入命令:npm install react-router-dom --save 默认安装的式router6.x。
  2. 简单使用
import { HashRouter, Routes, Route } from "react-router-dom";
ReactDOM.render(
  <HashRouter>
	<Routes>
	  <Route path="/home" element={<Home />}></Route>
	  <Route path="/" element={<Login />}></Route>
	</Routes>
  </HashRouter>,
  document.getElementById("root")
);
  1. 配置路由
//routers.tsx
const staticRoute = [
  {
    path: '/',
    element: <Home />,
    children: [
      {
        path: 'about',
        element: <div>About</div> 
      },
      {
        path: 'contact',
        element: <div>Contact</div>
      }
    ]
  },
  {
    path: '/login',
    element: <Login />,
  }
]

export default staticRoute;

//index.tsx
root.render(
  // <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  // </React.StrictMode>
);

//App.tsx
function App() {
  const elements = useRoutes(staticRoute);
  return (
    <div className="App">
      <div>
        <NavLink to="/">Home</NavLink> |
        <NavLink to="/login">login</NavLink> |
      </div>
      <div>
        {elements}
      </div>
    </div>
  );
}

//Home.tsx 配置子路由
import { Outlet } from "react-router-dom";
function Home() {
  return (
    <div>
      <h1>Home</h1>
      
      <div>
        <Outlet />
      </div>
    </div>
  );
}

export default Home;      

react hook

  1. useState
// Hook.tsx 界面
//useState 状态
const [people, setPeople] = useState<{ name: string, age: number }>({ name: "张三", age: 1 })
//方法
  const handleUpdate = () => {
    setPeople({ ...people, name: '王五' });
  }

//使用
<h1>{people.name}</h1>
<button onClick={handleUpdate}> change name </button>
  1. useRef
  const inputEl = useRef<HTMLInputElement>(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  
  //界面
  <input ref={inputEl} type="text" />
  <button onClick={onButtonClick}>Focus the input</button>

  // wrong! 注意:无法直接通过ref来引用函数组件,因为函数组件没有对象
  // <FunctionComponent ref={ref}></FunctionComponent>
  1. useEffect
useEffect(() => {
  console.log("useEffect--当people改变时");
}, [people]);

useEffect(() => {
  console.log("useEffect--弟一次加载时");
}, []);

useEffect(() => {
  console.log("useEffect--界面更新时");
});

useEffect(() => {
  //这边返回的清除函数的执行时机:
  //是在下一次函数组件re-render之后,useEffect之前执行
  console.log("useEffect--清除函数执行了");
  return () => {
      console.log("清除函数执行了");
  }
}, []);

  1. 缓存变量 useMemo

useMemo类似于Vue的计算属性,如果有一些属性值是可以根据其他值推导出来的,我们就可以使用useMemo
它的参数有两个:
一个函数,函数的返回值就是useMemo的结果
数组依赖项,表示触发第一个函数参数的条件

const [count, setCount] = useState(100);
  //useMemo()可以实现类似于Vue中的计算属性的功能,还可以用来缓存数据
  const obj = {
    name: "zhangsan",
  };
//下面的obj2在函数组件每一次重新渲染的时候都是同一个对象,没有重新初始化
  const obj2 = useMemo(() => {
    return {
      name: "zhangsan",
    };
  }, []);

//jsx////////////////
<button onClick={() => setCount(count + 1)}>点我修改count</button>
{
  // 点击按钮以后,由于函数会重新执行,父组件中将创建一个新的obj,传递给子组件的obj也会改变
    // 因此会触发子组件的自动更新
  }
<Son obj={obj}></Son>
    
{
    // 不会触发更新        
}   
<Son obj={obj2}></Son>  
  1. memo
  • memo() 是一个高阶组件,高阶组件其实就是一个函数,只不过这个函数的参数是一个组件,函数的返回值是一个新的组件
  • 只要父亲传递给孩子的props发生了变化就应该刷新子组件(如果父亲没有给子组件传递props或者父亲给子组件传递的props没有改变,则子组件不应该刷新
  • 父组件如果使用了useMemo,子组件一定要配套使用memo函数
function Son(props: IProps){
  console.log("Son render");
  return (
    <div>
      Son
    </div>
  );
};

export default memo(Son);
  1. 缓存函数 useCallback
const [text,setText] = useState("")
const changeHandler = useCallback((event:React.ChangeEvent<HTMLInputElement>)=>{
    setText(event.target.value)
},[])

//界面
<InputSun onChange={changeHandler}></InputSun>

  1. useContext

跨级组件通信,实现同一子树下所有节点可统一共享子树根节点的数据

//src/context/index.ts
type ContextType = {
  name: string,
  age: number
}
let context: Context<ContextType | null>  = createContext<ContextType | null>(null)
// 暴露出来一个 context 对象
export default context;

//父组件////////////////////
<div>
  <Context.Provider
    value={{name: "黑猫几绛", age: 100}}
  >
    <ContextSon></ContextSon>
  </Context.Provider>
</div>

// 子组件 ////////////////////////
function ContextSon() {
  // 在子组件中获取根组件暴露的数据
  const contextValue = useContext(Context);
  return (
    <div>
      <div>text文本为:{contextValue?.name}</div>
    </div>
  )
}
  1. useReducer

useReducer是在函数组件中实现类似 Redux 功能的一个Hook。他接收两个参数,第一个参数是一个recuder(纯函数),第二个参数是state的初始值。
他返回一个状态 state和 dispath,state是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的。

const reducer = (state: StateType, action: any) => {
    switch (action.type) {
      case "ADD":
        return { count: state.count + 1 };
      case "SUB":
        return { count: state.count - 1 };
      default:
        return state;
    }
  };
  // 使用useReducer(纯函数) ,得到state和dispatch
  const [state, dispatch] = useReducer(reducer, { count: 1000 });

// jsx ////////////
<div>
  {state.count}
  <button onClick={() => dispatch({ type: "ADD" })}>点我+1</button>
  <button onClick={() => dispatch({ type: "SUB" })}>点我-1</button>
</div>  
  1. useReduce & useContext 实现全局数据共享

useContext负责向子孙组件暴露数据
useReducer提供全局的state、reducers、dispatch等

  1. 自定义 Hooks
import React, { useState, useCallback, useEffect } from "react";

export const useWinSize = () => {
  // 1. 使用useState初始化窗口大小state
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight
  });

  const changeSize = useCallback(() => {
    // useCallback 将函数缓存起来 节流
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight
    });
  }, []);
  // 2. 使用useEffect在组件创建时监听resize事件,resize时重新设置state (使用useCallback节流)
  useEffect(() => {
    //绑定一次页面监听事件 组件销毁时解绑
    window.addEventListener("resize", changeSize);
    return () => {
      window.removeEventListener("resize", changeSize);
    };
  }, []);

  return size;
};

redux

  1. 安装依赖
npm install redux react-redux redux-thunk --save
npm install @types/react-redux --save-dev
// redux 与 redux-thunk有问题,所有降级了。20240123
npm install redux@4.2.1 --save

  1. redux 核心代码
//redux/store.js
import { legacy_createStore as createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import {thunk} from 'redux-thunk';

export default createStore(
	rootReducer,
	applyMiddleware(thunk)
);

//redux/index.js
import { combineReducers } from 'redux';
import dictionary from './dictionary';

const rootReducer = combineReducers({
	dictionary
});

export default rootReducer;

//redux/dictionary.js
import { SECURITY_LEVEL, METADATA_TYPE, DATA_TYPE, VALUE_TYPE } from '../types/dictionary';

const init_state = {
	levelType: [], //安全级别
	metaType: [], //元数据类型
	dataType: [], //来源类型-类型
	valueType: [] //取值范围类型
};

export default function dictionary(state = init_state, action:any) {
	const { type, data } = action;
	switch (type) {
		case SECURITY_LEVEL:
			return Object.assign({}, state, { levelType: data });
		case METADATA_TYPE:
			return Object.assign({}, state, { metaType: data });
		case DATA_TYPE:
			return Object.assign({}, state, { dataType: data });
		case VALUE_TYPE:
			return Object.assign({}, state, { valueType: data });
		default:
			return state;
	}
}

//redux/types/dictionary.js
export const INIT_LANG = 'INIT_LANG'; //示例
export const SECURITY_LEVEL = 'SECURITY_LEVEL';
export const METADATA_TYPE = 'METADATA_TYPE';
export const DATA_TYPE = 'DATA_TYPE';
export const VALUE_TYPE = 'VALUE_TYPE';

  1. 入口文件引入
import store from './redux/store';
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
);
  1. 组件中使用
import { initRoutes,getSecurityLevelList } from '../redux/actions/dictionary';
import { useSelector, useDispatch } from 'react-redux';

function Redux() {
  const dispatch = useDispatch();
  const levelType = useSelector((state:any) => state.dictionary.levelType); // 示例
  const metaType = useSelector((state:any) => state.dictionary.metaType); //
  
  useEffect(() => {
		initDict();
	// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);
	const initDict = () => {
		dispatch(initRoutes(['aaa']));
	};

  const onButtonClick = () => {
    dispatch(initRoutes(['leolee']));
  };
  const onButtonClick2 = () => {
		dispatch(getSecurityLevelList());
  };

  return (
    <>
      <div>redux</div>
      <div>{levelType[0]}</div>
      <div>{metaType}</div>
      <button onClick={onButtonClick}>修改Redux</button>
      <button onClick={onButtonClick2}>修改Redux2</button>
    </>
  );
}

源码下载:https://gitee.com/leolee18/react-project-demo.git