Skip to content

react学习笔记

警告

本教程使用了tailwindcss如何使用?

react是一个javascript库,与vue不同,react是一个组件化的库,vue是一个MVVM的库。

特点:

  • 1.声明式设计 −React采用声明范式,可以轻松描述应用。

  • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

  • 3.灵活 −React可以与已知的库或框架很好地配合。

  • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

  • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

  • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

1. 如何编写一个jsx文件

jsx
import {useState} from "md/framework/react/react";

function Home() {
    const [count, setCount] = useState(0);

    const btnStyle = {
        backgroundColor: "transparent",
        color: "#000",
        padding: "10px",
        borderRadius: "5px",
        cursor: "pointer",
        margin: "10px",
        boxShadow: "0 0 5px rgba(0,0,0,0.3)",
    };

    return (
        <>
            <div
                className="home"
                style={btnStyle}
                onClick={() => setCount(count + 1)}
            >
                <span>{count}</span>
            </div>
        </>
    );
}

export default Home;

警告

  • 逻辑不越界:声明和逻辑等操作要在函数内完成
  • 返回值不缺失:必须得有返回值
  • <></>类似于<div></div>,可以不写标签,但必须得有返回值
  • 事件处理:传递的是一个函数的引用,而不是函数结果 如:onClick={() => setCount(count + 1)}而不能是onClick={setCount(count + 1)}
    诺想不这么麻烦,可以定义一个方法:
    jsx
      const handleClick = () => {
        setCount(count + 1)
      }
    
      <div
          className="home"
          style={btnStyle}
          onClick={handleClick}
          >
            <span>{count}</span>
      </div>

useState

useState允许你在函数组件中使用局部状态。它返回一个状态值和更新该状态值的函数。

jsx

import {useState} from "react";

function UseState() {
    const [count, setCount] = useState(0);
    const [list, setList] = useState([0, 1]);

    return (
        <>
            <h1>{count}</h1>
            <ul>
                {list.map((item, index) => (
                    <li key={index}>
                        {item}
                    </li>
                ))}
            </ul>
            <div>
                <button
                    onClick={() => setCount(count + 1)}
                >
                    +1
                </button>
                <button
                    onClick={() => setList([...list, list.length])}
                >
                    数组增加
                </button>
            </div>
        </>
    );
}

export default UseState;

警告

react的核心概念是【不可变数据】,react使用的是浅比较,只有数据完全变化才会重新渲染。

浅比较:

  • 对于基础类型,会比较数值和数据类型,如:1===1,1==='1'
  • 对于引用类型,还会比较内存地址,如:{}==={}

useEffect

用于监听数据变化,并执行副作用。

jsx
//   语法:
useEffect(() => {
// ...需要执行的副作用代码
    return () => {
// 可选的清理代码
    };
}, [依赖项数组]);
jsx
import {useState, useEffect} from "react";

export default function UseEffect() {
    const [count, setCount] = useState(0);
    const [old, setOldCount] = useState(0);
    const [newCount, setNewCount] = useState(0);

    useEffect(() => {
        //更新时执行
        setNewCount(count);
        return () => {
            // 当组件卸载时或当前 `effect` 依赖变化、下一次 `effect` 执行前
            setOldCount(count);
        };
    }, [count]);

    return (
        <>
            <div className=" box">{count}</div>
            <div className=" box flex my-10 gap-5">
                <div className=" box">旧值:{old}</div>
                <div className=" box">新值:{newCount}</div>
            </div>
            <button
                className="  p-5 py-2 bg-amber-100 my-5 rounded-xl"
                onClick={() => setCount(count + 1)}
            >
                增加
            </button>
        </>
    );
}

useContext()&&createContext()

用于跨组件传递数据。

jsx
import {createContext, useContext} from "react";

// 1. 创建上下文
const MyContext = createContext("默认数据");

// 2. 上层组件提供数据
function Parent() {
    return (
        <MyContext.Provider value="共享数据">// 提供数据
            <Child/>
        </MyContext.Provider>
    );
}

// 3. 下层组件消费数据
function Child() {
    const value = useContext(MyContext); // 读取上下文数据
    return <div className="box">{value}</div>; // 渲染:共享数据 (诺父组件未提供数据,则使用默认数据)
}

export default Parent;

useReducer

useReduceruseState 的替代方案,适用于复杂的状态逻辑。它接受一个 reducer 函数和初始状态,返回当前状态和 dispatch 函数。

jsx
import {useReducer} from "react";

// 定义 reducer 函数
function counterReducer(state, action) {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        case 'reset':
            return {count: 0};
        default:
            return state;
    }
}

function Counter() {
    const [state, dispatch] = useReducer(counterReducer, {count: 0});

    return (
        <>
            <div>Count: {state.count}</div>
            <button onClick={() => dispatch({type: 'increment'})}>+</button>
            <button onClick={() => dispatch({type: 'decrement'})}>-</button>
            <button onClick={() => dispatch({type: 'reset'})}>重置</button>
        </>
    );
}

export default Counter;

使用场景

  • 状态逻辑复杂,有多个子值
  • 下一个状态依赖于前一个状态
  • 需要优化性能(可以传递 dispatch 而不是回调函数)

useMemo

useMemo 用于缓存计算结果,只有当依赖项变化时才重新计算。

jsx
import {useState, useMemo} from "react";

function ExpensiveComponent({items}) {
    // 只有 items 变化时才重新计算
    const sortedItems = useMemo(() => {
        console.log('重新计算排序');
        return items.sort((a, b) => a - b);
    }, [items]);

    return (
        <ul>
            {sortedItems.map(item => (
                <li key={item}>{item}</li>
            ))}
        </ul>
    );
}

export default ExpensiveComponent;

使用场景

  • 昂贵的计算操作
  • 避免子组件不必要的重新渲染(配合 React.memo)

useCallback

useCallback 用于缓存函数引用,只有当依赖项变化时才返回新的函数。

jsx
import {useState, useCallback, memo} from "react";

const Child = memo(({onClick, name}) => {
    console.log(`${name} 组件重新渲染`);
    return <button onClick={onClick}>点击</button>;
});

function Parent() {
    const [count1, setCount1] = useState(0);
    const [count2, setCount2] = useState(0);

    // 缓存函数,避免 Child 组件不必要的重新渲染
    const handleClick1 = useCallback(() => {
        setCount1(count1 + 1);
    }, [count1]);

    const handleClick2 = useCallback(() => {
        setCount2(count2 + 1);
    }, [count2]);

    return (
        <>
            <Child onClick={handleClick1} name="Child1" />
            <Child onClick={handleClick2} name="Child2" />
            <div>Count1: {count1}, Count2: {count2}</div>
        </>
    );
}

export default Parent;

使用场景

  • 将函数作为 props 传递给子组件
  • 作为其他 Hook 的依赖项

useRef

useRef 用于创建一个可变的引用对象,返回的 ref 对象在组件的整个生命周期内保持不变。

jsx
import {useRef, useEffect} from "react";

function RefExample() {
    const inputRef = useRef(null);
    const countRef = useRef(0);

    useEffect(() => {
        // 聚焦输入框
        inputRef.current?.focus();
    }, []);

    const handleClick = () => {
        countRef.current += 1;
        console.log('点击次数:', countRef.current);
        // 注意:修改 countRef.current 不会触发重新渲染
    };

    return (
        <>
            <input ref={inputRef} type="text" />
            <button onClick={handleClick}>点击</button>
        </>
    );
}

export default RefExample;

使用场景

  • 访问 DOM 元素
  • 存储可变值(不触发重新渲染)
  • 保存前一个值

useImperativeHandle

useImperativeHandle 用于在使用 ref 时自定义暴露给父组件的实例值。

jsx
import {forwardRef, useImperativeHandle, useRef} from "react";

const Child = forwardRef((props, ref) => {
    const inputRef = useRef(null);

    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current?.focus();
        },
        getValue: () => {
            return inputRef.current?.value;
        }
    }));

    return <input ref={inputRef} type="text" />;
});

function Parent() {
    const childRef = useRef(null);

    const handleFocus = () => {
        childRef.current?.focus();
    };

    const handleGetValue = () => {
        const value = childRef.current?.getValue();
        console.log('输入值:', value);
    };

    return (
        <>
            <Child ref={childRef} />
            <button onClick={handleFocus}>聚焦</button>
            <button onClick={handleGetValue}>获取值</button>
        </>
    );
}

export default Parent;

自定义 Hooks

自定义 Hook 是一个函数,其名称以 "use" 开头,函数内部可以调用其他的 Hook。

jsx
import {useState, useEffect} from "react";

// 自定义 Hook:用于获取窗口大小
function useWindowSize() {
    const [size, setSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight
    });

    useEffect(() => {
        const handleResize = () => {
            setSize({
                width: window.innerWidth,
                height: window.innerHeight
            });
        };

        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
    }, []);

    return size;
}

// 使用自定义 Hook
function WindowSizeDisplay() {
    const {width, height} = useWindowSize();

    return (
        <div>
            窗口大小: {width} x {height}
        </div>
    );
}

export default WindowSizeDisplay;

组件通信

1. 父子组件通信

父传子(Props)

jsx
// 父组件
function Parent() {
    const message = "Hello from Parent";
    return <Child message={message} />;
}

// 子组件
function Child({message}) {
    return <div>{message}</div>;
}

子传父(回调函数)

jsx
// 父组件
function Parent() {
    const [count, setCount] = useState(0);

    const handleChildClick = (value) => {
        setCount(value);
    };

    return <Child onClick={handleChildClick} />;
}

// 子组件
function Child({onClick}) {
    return (
        <button onClick={() => onClick(100)}>
            传递值给父组件
        </button>
    );
}

2. 兄弟组件通信

通过共同的父组件进行状态提升:

jsx
function Parent() {
    const [sharedData, setSharedData] = useState('');

    return (
        <>
            <Child1 onUpdate={setSharedData} />
            <Child2 data={sharedData} />
        </>
    );
}

function Child1({onUpdate}) {
    return (
        <button onClick={() => onUpdate('来自 Child1 的数据')}>
            更新数据
        </button>
    );
}

function Child2({data}) {
    return <div>接收到的数据: {data}</div>;
}

3. 跨组件通信(Context)

jsx
import {createContext, useContext, useState} from "react";

const DataContext = createContext();

function App() {
    const [data, setData] = useState('共享数据');

    return (
        <DataContext.Provider value={{data, setData}}>
            <Component1 />
            <Component2 />
        </DataContext.Provider>
    );
}

function Component1() {
    const {data, setData} = useContext(DataContext);
    return (
        <div>
            <p>{data}</p>
            <button onClick={() => setData('Component1 更新的数据')}>
                更新数据
            </button>
        </div>
    );
}

function Component2() {
    const {data} = useContext(DataContext);
    return <div>接收到的数据: {data}</div>;
}

性能优化

1. React.memo

用于函数组件,类似于 PureComponent,只在 props 变化时重新渲染。

jsx
import {memo} from "react";

const ExpensiveComponent = memo(({name, count}) => {
    console.log('组件重新渲染');
    return (
        <div>
            {name}: {count}
        </div>
    );
}, (prevProps, nextProps) => {
    // 自定义比较函数(可选)
    return prevProps.name === nextProps.name && 
           prevProps.count === nextProps.count;
});

export default ExpensiveComponent;

2. useMemo 和 useCallback

配合使用避免不必要的计算和重新渲染。

jsx
function OptimizedParent({items}) {
    const [filter, setFilter] = useState('');

    // 缓存计算结果
    const filteredItems = useMemo(() => {
        return items.filter(item => item.includes(filter));
    }, [items, filter]);

    // 缓存函数引用
    const handleClick = useCallback(() => {
        console.log('点击事件');
    }, []);

    return (
        <>
            <input 
                value={filter} 
                onChange={(e) => setFilter(e.target.value)} 
            />
            <ExpensiveList items={filteredItems} onClick={handleClick} />
        </>
    );
}

3. 代码分割(React.lazy)

jsx
import {lazy, Suspense} from "react";

// 懒加载组件
const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
    return (
        <Suspense fallback={<div>加载中...</div>}>
            <LazyComponent />
        </Suspense>
    );
}

生命周期对比

类组件生命周期

生命周期方法函数组件等价物
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {})
componentWillUnmountuseEffect(() => { return () => {} })

函数组件生命周期示例

jsx
import {useEffect} from "react";

function LifecycleExample() {
    useEffect(() => {
        // componentDidMount
        console.log('组件挂载');

        return () => {
            // componentWillUnmount
            console.log('组件卸载');
        };
    }, []);

    useEffect(() => {
        // componentDidUpdate
        console.log('组件更新');
    });

    return <div>生命周期示例</div>;
}

注意事项

  1. 不要在循环、条件或嵌套函数中调用 Hooks
  2. 只在 React 函数组件或自定义 Hook 中调用 Hooks
  3. 使用依赖数组时要包含所有依赖项
  4. 避免在 useEffect 中修改 state 导致无限循环
  5. 合理使用 useMemo 和 useCallback,不要过度优化

最后更新:2025年

这是我的个人文档