在React函數組件中,是默認只有屬性沒有狀態的,咱們能夠使用以下代碼用useState讀寫變量和設定初始值 :react
const [n,setN] = react.useState(0)
redux
const [user,setUser] = react.useState({name:'Jack'})
數組
💡 注意事項bash
咱們沒法局部setState,由於react函數組件不會幫咱們合併屬性。解決辦法是使用react提供的...
語法將原屬性拷貝並放在更新值以前。ide
由於setState(obj)
中obj
若是是在原來的對象上進行修改,那麼對象的地址沒有更改,react會將這個對象斷定爲原對象,則更改無效。解決方法是始終設置新的對象就能夠了。函數
① useState的初始值並不侷限於對象,能夠用函數的形式設定。佈局
const [state, setState] = useState(()=>{ return initialState })
ui
② 因爲表達式的侷限性,若是你須要在setState中進行屢次操做,那麼須要使用函數的形式以確保每一次的操做是有效的。spa
const onClick = ()=>{
setN(n+1) //無效操做
setN(n+1) //你會發現 n 不能加 2
}
複製代碼
const onClick = ()=>{
setN(i=>i+1) //有效操做
setN(i=>i+1) //最後值爲n+2
}
複製代碼
若是你發現有幾個變量須要彙總在一塊兒,你能夠使用複雜版的useState —— useReducer,它的好處就是能夠對這個對象進行一個總體的操做 。而且能夠踐行React社區一直推崇的Flux/Redux思想,在我看來這個思想可能會在不久的未來Hooks思想盛行以後逐漸被取代,可是目前仍是須要了解這個被普遍應用的Hook。翻譯
使用useReducer分爲四個步驟:
initialState
reducer(state,action)
useReducer
獲得讀和寫API({type:"操做類型"})
一個🌰:
const initial = {
n:0
};
const reducer = (state,action) => {
if(action.type === "add") {
return { n:state.n + 1 };
} 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: "multi", number: 2 });
};
return (
<div className = "App">
<div>n: {n}</div>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>*2</button>
</div>
);
}
複製代碼
如何代替redux?
這個Hook翻譯過來就是「上下文」。何爲上下文?上下文就是你在運行一個程序的時候所要知道的全部變量。全局變量就是全局的上下文,上下文是局部的全局變量。上一個Hook中咱們已經瞭解到useContext的用法。
一個🌰:
const C = createContext(null);
function App() {
const [n, setN] = useState(0);
return (
<C.Provider value={{ n, setN }}>
<div className="App">
<Dog />
</div>
</C.Provider>
);
}
function Dog() {
const { n, setN } = useContext(C);
return (
<div>
狗 n: {n} <Child />
</div>
);
}
function Child() {
const { n, setN } = useContext(C);
const onClick = () => {
setN(i => i + 1);
};
return (
<div>
狗兒子 n: {n}
<button onClick={onClick}>+1</button>
</div>
);
}
複製代碼
這個Hook翻譯過來爲「反作用」,我認爲把它叫作afterRender更好,由於這是每次render以後就會調用的一個函數。 用途:
做爲componentDidMount(出生後)使用
useEffect(()=>{console.log('hi'),[]}) //使用空數組表示第一次渲染時
做爲componentUpdate(更新)使用
useEffect(()=>{console.log('hi'),[n]}) //表示當{n}變化時
useEffect(()=>{console.log('hi')}) //表示每一次更新時
做爲componentWillUnmount(將死)使用
useEffect(()=>{console.log('hi')})
return ()=>{} //添加return表示當組件將時執行某操做
複製代碼
useLayoutEffect
因爲react默認會有多餘的render出現,若是props不變就沒要再次執行這個函數組件,useMemo就是阻止函數組件無效執行的Hook。
❗❗ 若是在函數組件中添加一個監聽事件,則useMemo會直接無效,須要寫成:
useMemo(()=> (x) => console.log(x))
這種形式,一個返回函數的函數。 因爲這種方式難用且難以理解,因而誕生了useCallback。
useCallback(x => log(x), [m]) //等價於下面一種寫法
useMemo(() => x => log(x), [m])
複製代碼
若是你須要一個值在組件不斷render的時候保持始終是同一個值地址不變,那就須要用到useRef。
首先須要初始化:const n = useRef(0)
讀取時則是:n.current
(這裏的current是用來保證兩次useRef的是相同的值的引用)
❗❗ useRef是不能作到變化的時候自動render的,由於這不符合react的理念,若是你想要這個功能,徹底能夠本身加,監聽ref,當ref.current變化的時候,調用setX便可。
自定義Hook必須以use開頭,這是react既定的語法。經過自定義 Hook,能夠將組件邏輯提取到可重用的函數中。
一個🌰:
function App() {
const { list } = useList();
return (
<div className="App">
<h1>List</h1>
{list ? (
<ol>
{list.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ol>
) : (
"加載中..."
)}
</div>
);
}
複製代碼
與 React 組件不一樣的是,自定義 Hook 不須要具備特殊的標識。咱們能夠自由的決定它的參數是什麼,以及它應該返回什麼(若是須要的話)。換句話說,它就像一個正常的函數,咱們能夠最大限度的靈活使用這個Hook。