介绍
1 2 3 4 5
| // 创建项目(官方 create-react-app 已废弃,但不影响学习) npx create-react-app xxx
// JSX 是JS的语法扩展,浏览器不能识别,需要通过解析工具解析之后在浏览器中运行 // https://babeljs.io 编译参考
|
基础语法
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
| const message = 'this is message' function getAge() { return 18 }
export default function App(){ return ( <div> <h1>this is title</h1> {/* 字符串识别 */} {'this is str'} {/* 变量识别 */} {message} {/* 函数调用 渲染为函数的返回值 */} {getAge()} </div> ) }
const list = [ {id:1001, name:'Vue'}, {id:1002, name: 'React'}, {id:1003, name: 'Angular'} ]
function App(){ return ( <ul> {list.map(item=><li key={item.id}>{item}</li>)} </ul> ) }
const flag = true const loading = false
function App(){ return ( <> {flag && <span>this is span</span>} {loading ? <span>loading...</span>:<span>this is span</span>} </> ) }
const type = 1 function getArticleJSX(){ if (type === 0) return <div>无图模式模版</div> else (type === 3) return <div>三图模式模版</div> }
function App() { return (<>{ getArticleJSX() }</>) }
function App(){ const clickHandler = (e, value)=> { console.log('button按钮点击了') console.log('值', e, value) } return ( <button onClick={clickHandler}>click me</button> <button onClick={(e) => clickHandler(e, 1)}>click me</button> ) }
function Button(){ return <button>click me</button> }
function App(){ return ( <div> {/* 自闭和 */} <Button/> {/* 成对标签 */} <Button></Button> </div> ) }
function App() { const [ count, setCount ] = useState(0) return ( <div> {/* setCount 里面别写 count++ 不修改源数据 */} <button onClick={()=>setCount(count+1)}>{ count }</button> </div> ) }
const [num, setNum] = useState(() => { const num1 = 1 + 2; const num2 = 2 + 3; return num1 + num2 });
setCount(count + 1); console.log(count); setTimeout(() => { console.log(count); });
setCount(count + 1); setCount(count + 1); setCount((prevCount) => { return prevCount + 1; });
let showCount = 0 if(count) { showCount = count }
npm install immer use-immer import { useImmer, useImmerReducer } from 'use-immer'; const [person1, setPerson1] = useState({ work: { city: "a", }, }); const [person2, updatePerson2] = useImmer({ work: { city: "a", }, });
setPerson1({ ...person1, work: { city: "b", } }) updatePerson2((draft) => { draft.work.city = "b"; });
function reducer(draft, action) { switch (action.type) { case 'ADD_TODO': draft.todos.push({ id: Date.now(), text: action.text, done: false }); break; case 'TOGGLE_TODO': const todo = draft.todos.find(t => t.id === action.id); if (todo) todo.done = !todo.done; break; } } const [state, dispatch] = useImmerReducer(reducer, initialState);
return (<div className={xxx === xxx ? 'nav-item active' : 'nav-item'}></div>) return (<div className={[xxx === xxx ? 'nav-item active' : '', 'nav-item'}].filter(Boolean).join(' '))}]></div>)
import classNames from 'classnames' className={classNames('nav-item', { active: type === item.type })}
<div style={{color: 'red', fontSize: '16px'}}>xxx</div> const styleObj = { color: 'red', 'font-size': '16px' } <div style={styleObj}></div>
|
useRef 获取 DOM
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 32 33
| function App(){ const inputRef = useRef(null)
const onChange = ()=>{ console.log(inputRef.current, inputRef.current.value) } return ( <input type="text" ref={inputRef} onChange={onChange} /> ) }
import { useRef } from 'react'; function MyComponent() { const instanceVar = useRef({ count: 0 });
const handleClick = () => { instanceVar.current.count += 1; console.log('Current count (no re-render):', instanceVar.current.count); };
return <button onClick={handleClick}>Click me</button>; }
|
组件基础
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
function Son(props){ return <div>{ props.name }, {props.child}</div> } function App(){ const name = 'this is app name' return ( <div> <Son name={name} child={<span>test</span>}/> </div> ) }
function Son(props) { return <div> {props.children}</div>; } function App() { return ( <div> <Son> <span>child1</span> <span>child2</span> </Son> </div> ); }
import { useState, forwardRef, useRef, useImperativeHandle } from 'react'; const SubCom = forwardRef((props, ref) => { const [val, setVal] = useState('x'); const onTest = () => { console.log('onTest'); }; useImperativeHandle(ref, () => ({ val, onTest, })); return ( <div> <input type="text" value={val} onChange={(e) => setVal(e.target.value)} /> </div> ); });
const ForwardRef = () => { const subComRef = useRef(null);
return ( <> <div>ForwardRef</div> <SubCom ref={subComRef} /> <button onClick={() => console.log(subComRef.current)}>LOG</button> <button onClick={() => subComRef.current?.onTest()}>调用子组件方法 - onTest</button> </> ); }
import { createContext, useContext } from "react"
const MsgContext = createContext() function A () { return ( <div> this is A component <B /> </div> ) } function B () { const msg = useContext(MsgContext) return ( <div> this is B compnent,{msg} </div> ) } function App () { const msg = 'this is app msg' return ( <div> {/* 2. 在顶层组件 通过Provider组件提供数据 */} {/* 固定的 value 写法,多个值使用对象 */} <MsgContext.Provider value={msg}> this is App <A /> </MsgContext.Provider> </div> ) }
<MsgContext.Consumer> {(value) => { return () }} </MyContext.Consumer>
<Son {...xxx} />
|
useEffect
介绍:用在 React 组件中创建不是由事件引起而是由渲染本身引起的操作(副作用), 比如发送请求,更改 DOM 等等。
- 参数1 是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
- 参数2 是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
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 32 33
|
function App() { const [count, setCount] = useState(0); useEffect(() => { setCount(1); }, []); return <div>Count: {count}</div>; }
function Son () { useEffect(() => { const timer = setInterval(() => { console.log('定时器执行中...') }, 1000)
return () => { clearInterval(timer) } }, []) return <div>this is son</div> }
|
useLayoutEffect(同步副作用)
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
|
useLayoutEffect(() => { const rect = ref.current.getBoundingClientRect(); setHeight(rect.height); }, []);
useLayoutEffect(() => { inputRef.current?.focus(); }, []);
useLayoutEffect(() => { gsap.set(ref.current, initialState); startAnimation(); }, []);
|
useReducer
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 32 33 34 35 36 37 38
| const [state, dispatch] = useReducer(reducer, initialArg, init?)
const initialState = { count: 0 };
function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } }
const App = () => { const [state, dispatch] = useReducer(reducer, initialState);
return ( <> Count: {state.count} <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <button onClick={() => dispatch({ type: 'increment' })}>+</button> </> ); } export default App;
const [state, dispatch] = useReducer(reducer, createInitialState(props));
const [state, dispatch] = useReducer(reducer, props, createInitialState);
|
memo + useMemo + useCallback
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const Child = React.memo(({ style, onClick }) => { console.log('rendered'); return <div onClick={onClick} style={style}>子组件</div>; });
const handleClick = useCallback(() => { console.log('click') }, []); return <Child onClick={handleClick} />;
const sortedList = useMemo(() => { return heavySort(list, criteria); }, [list, criteria]);
const style = useMemo(() => ({ fontSize: size }), [size]); <Child style={style} label="size 改变才会渲染" />
|
自定义 Hook
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
| function useToggle () { const [value, setValue] = useState(true)
const toggle = () => setValue(!value)
return { value, toggle } }
function App () { const { value, toggle } = useToggle() return ( <div> {value && <div>this is div</div>} <button onClick={toggle}>toggle</button> </div> ) }
只能在组件中或者其他自定义 Hook 函数中调用 只能在组件的顶层调用,不能嵌套在 if、for、其它的函数中
|
Module CSS
1 2 3 4 5 6 7 8 9 10 11
| // 嵌套写法 .father { .app5Container {} } // 使用方式 <div className={styles.father}> <div className={styles.app5Container}>生效</div> </div>
// global 语法 :global(.father){ }
|
闭包陷阱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { useEffect, useState } from 'react';
function App() { const [count,setCount] = useState(0);
useEffect(() => { setInterval(() => { console.log(count); setCount(count + 1); }, 1000); }, []); return <div>{count}</div> } export default App;
|
Redux
1 2
| // 参考:https://juejin.cn/post/7101688098781659172 @reduxjs/toolkit、react-redux
|
路由