React 函数组件

[React] 函数组件

创建一个函数组件

1
2
3
4
5
6
7
8
9
const Hello = (props) => {
return <div>{props.message}</div>
}

const Hello = props => <div>{props.message}</div>

function Hello(props){
return <div>{props.message}</div>
}

Hooks

useState

  1. setN一定会触发重新渲染

  2. 数据会被存入新的n

1
2
3
4
5
6
7
8
9
10
11
function App() {
const [n, setN] = React.useState(0);
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {
const [n, setN] = useState(0)
const onClick = ()=>{
setN(n+1)
setN(n+1) // 你会发现 n 不能加 2
// setN(i=>i+1)
// setN(i=>i+1) n可以+2
}
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+2</button>
</div>
);
}

useReducer

Flux/Redux思想

useState的复杂版,常用于表单

  1. 创建初始值

  2. 创建操作合集reducer(state,action)

  3. 传给useReducer,得到读写API

  4. 调用写API,传一个type ({type:‘操作类型’})

代替Redux

  1. 将数据集中在一个store对象

  2. 将操作集中在reducer

  3. 创建一个Context

  4. 创建对数据的读写api

  5. 将读写api放入Context

  6. 用Context.Provider把api提供给所有组件

  7. 各个组件用useContext获取读写api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const initial = {
n: 0
};

const reducer = (state, action) => {
if (action.type === "add") {
return { n: state.n + action.number };
} else if (action.type === "multi") {
return { n: state.n * 2 };
} else {
throw new Error("unknown type");
}
};

function App() {
const [state, dispatch] = useReducer(reducer, initial);
const { n } = state;
const onClick = () => {
dispatch({ type: "add", number: 1 });
};
const onClick2 = () => {
dispatch({ type: "add", number: 2 });
};
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>+2</button>
</div>
);
}

useContext

上下文,相当于在某一范围中充当「全局变量」的作用

从上至下逐级更新的过程,不是响应式过程(监听数据变化并做出反应)

  1. 创建一个Context

    1
    const C = createContext(null)
  2. 用C.Provider圈定范围

    1
    2
    3
    4
    5
    <C.Provider value={}>
    const [ n, setN ] = useState(0)
    <ComponentA />
    <ComponentB />
    </C.Provider>
  3. 在组件中使用

    1
    const { n, setN } = useContext(C)

useEffect

每次render后调用的函数

在函数组件中,作为componentDidMount, componentDidUpdate, componentWillUnmount使用

多个useEffect会按写代码时的先后顺序执行

1
2
3
4
5
6
7
8
9
useEffect(() => {})  //每次render后执行

useEffect(() => {}, []) //第一次渲染后执行

useEffect(() => {}, [n]) //n变化后执行
//如果数组中包含了所有的变量,那与第一种写法等价

useEffect(() => { return () => {}}) //return的函数会在该组件被噶了之前执行

useLayoutEffect

在浏览器改变外观前执行,区别于useEffect在浏览器改变外观之后

因此useLayoutEffect总是比useEffect先执行

useMemo

React.memo(ComponentA): ComponentA只在props更新后执行

1
2
3
const fn = useMemo(() => {
return () => {}
}, [m,n])

缓存内容,在页面刷新时缓存上一次的值,并使新渲染的组件使用该缓存值,新fn和旧fn为相同的两个空函数。只有m或者n变化时,fn才会重新得到新值

如果不使用useMemo,两次得到的fn会因为函数地址不同而被认为是两个不同的函数,从而触发组件的渲染,增加了渲染量,降低了性能

useCallback

useMemo简化版

1
const fn = useCallback(() => {}, [m,n])

useRef

让一个值在组件不断render时保持不变

1
2
3
4
5
const count = useRef(0)

useEffect(()=>{
count.current += 1
})

此时count记录了组件渲染的次数

使用.current的原因是:使用引用,来确保每次的渲染得到的新count都是指向同一个对象的引用

注意:count.current的变化不会触发UI的更新,需要调用useState来手动更新

1
2
3
4
5
6
7
8
9
const [随便取一个,set随便取一个] = useState(null)

const onClick = () => {
count.current += 1
set随便取一个(Math.random())
}

//这样才会触发UI更新

forwardRef

React的函数组件需要使用forwardRef来获取外部传来的ref

1
2
3
4
5
6
7
8
9
10
11
12
function App() {
const buttonRef = useRef(null)
return(
<div>
<Button2 ref={buttonRef}>button</Button2>
</div>
)
}

const Button2 = React.forwardRef((props,ref) => {
return (<button ref={ref} {...props} />)
}

useImperativeHandle

自定义ref


React 函数组件
https://bald3r.wang/2022/07/24/29-React-函数组件/
作者
Allen
发布于
2022年7月24日
许可协议