useState
useState() returns an array with exactly two elements:
(1) Your current state value, (2) A method to update your state value.
useState() will replace the old state instead of merging.
import React, { useState } from "react";
import Person from "./Person/Person";
const App = props => {
const [personState, setPersonState] = useState({
person: [
{ name: "Harry", age: 24 }
]
});
const [otherState, setOtherState] = useState("some other value");
const switchNameHandler = () => {
setPersonState({
person: [
{name: "Harry Lu", age: 25}
]
});·
};
return (
<div className="App">
<button onClick={switchNameHandler}>Switch Name</button>
<Person
name={personState.person[0].name}
age={personState.person[0].age}
/>
</div>
);
}
export default App;
useEffect & useRef & useContext
useEffect: componentDidMount · componentDidUpdate
componentWillUnmount
import React, { useEffect, useRef, useContext } from 'react';
import AuthContext from '../../context/auth-context';
const cockpit = props => {
const toggleBtnRef = useRef(null);
const authContext = useContext(AuthContext);
useEffect(() => {
// Http request...
// same as componentDidMount and componentDidUpdate
// setTimeout(() => {
// alert('Saved data to cloud!');
// }, 1000);
toggleBtnRef.current.click();
return () => {
// clean-up code here
// same as componentWillUnmount
};
}, []); // only re-run the effect if the value inside changes
return (
<div>
<button ref={toggleBtnRef}>
Toggle Persons
</button>
<button onClick={authContext.login}>Log in</button>
</div>
);
};
export default React.memo(cockpit);
UseContext
recieved a React.createContext
return ---- <MyContext.Provider>
value
prop
const value = useContext(MyContext);
Counter Example
import React, { useState } from "react";
const App = (props) => {
const [counter, setCounter] = useState(0);
return (
<React.Fragment>
<span>You Clicked {counter} times!</span>
<button onClick={() => setCounter(counter + 1)}>Increase</button>
<button onClick={() => setCounter(counter - 1)}>Decrease</button>
</React.Fragment>
);
}
export default App;
React Counter using Hooks
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
function App() {
const [num, setNum] = useState(Number(localStorage.getItem("num")) || 0);
useEffect(() => {
localStorage.setItem("num", num);
}, [num]);
return (
<div>
{num}
<button onClick={() => setNum(num + 1)}>Add</button>
<button onClick={() => setNum(num - 1)}>Subtract</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Using the Effect Hook
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
useEffect
lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
Use Multiple Effects to Separate Concerns
A custom Hook is a JavaScript function whose name starts with ”use
” and that may call other Hooks.
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
Class component to functional
Hook Use
Only in top of react function. Not in conditional control language
react use linked list hooks to rank
const [name,setName] = useState('leo');
///
setName('Lily');
// Some code
// 进入 mounState 逻辑
function mountState(initialState) {
// 将新的 hook 对象追加进链表尾部
var hook = mountWorkInProgressHook();
// initialState 可以是一个回调,若是回调,则取回调执行后的值
if (typeof initialState === 'function') {
// $FlowFixMe: Flow doesn't like mixed types
initialState = initialState();
}
// 创建当前 hook 对象的更新队列,这一步主要是为了能够依序保留 dispatch
const queue = hook.queue = {
last: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
};
// 将 initialState 作为一个“记忆值”存下来
hook.memoizedState = hook.baseState = initialState;
// dispatch 是由上下文中一个叫 dispatchAction 的方法创建的,这里不必纠结这个方法具体做了什么
var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
// 返回目标数组,dispatch 其实就是示例中常常见到的 setXXX 这个函数,想不到吧?哈哈
return [hook.memoizedState, dispatch];
}
mountWorkInProgressHook :
function mountWorkInProgressHook() {
// 注意,单个 hook 是以对象的形式存在的
var hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
if (workInProgressHook === null) {
// 这行代码每个 React 版本不太一样,但做的都是同一件事:将 hook 作为链表的头节点处理
firstWorkInProgressHook = workInProgressHook = hook;
} else {
// 若链表不为空,则将 hook 追加到链表尾部
workInProgressHook = workInProgressHook.next = hook;
}
// 返回当前的 hook
return workInProgressHook;
}
Update: