两小时入门React(以TS和FC为主)
1. 创建React
项目
- js项目:
npx create-react-app js-react
- ts项目:
npx create-react-app ts-react --template typescript
创建成功后启动项目
cd ts-react
npm start
2.元素渲染
ReactDOM.render(element, contain[, callback])
- 如果提供了可选的回调函数,该回调将在组件被渲染或更新后执行
- 首次调用时,容器节点里的所有DOM元素都会被替换
- 不会修改容器节点(只会修改容器的子节点),可以在不覆盖现有子节点的情况下,将组件插入已有的DOM节点中
- 会返回对根组件
ReactComponent
实例的引用,应避免 - 组件名称必须大写开头字母
React
会将以小写字母开头的组件视为原生DOM标签 - 每次组件更新时
render
方法都会被调用,但只要在相同的DOM节点中渲染<Clock />
就仅有一个Clock
组件的class实例被创建使用
3. 数据绑定和事件绑定
数据的绑定和事件的绑定都是通过大括号{}
实现的,在TSX语法中,可以在大括号内放置任何有效的JavaScript表达式
<span className="demo-class">{ value }</span>
因为 JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用
camelCase
(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。
React元素的事件处理和DOM元素的很相似,但是有一点语法上的不同;
- React事件的命名采用小驼峰式(camelCase),而不是纯小写
- 使用JSX语法时你需要传入一个函数作为事件处理函数,而不是一个字符串
e.stopPropagation()
阻止附加到上述标签的事件处理程序触发e.preventDefault()
阻止具有它的少数事件的默认浏览器行为
更多的细节使用和描述在e.g上
import React, { FC } from "react";
const Demo: FC = () => {
const msg = "show something";
// e 是一个合成事件
const handleClick = (e: any): void => {
e.preventDefault(); // 阻止默认行为
alert("had clicked");
};
// 数组通过map或者for循环,返回一个列表对象,通过{}渲染
const demoList = [1, 2, 3, 4, 5, 5, 6].map((i) => (
// 通过箭头函数指定需要传递的参数,或者通过bind调用来隐式传递事件对象
<>
<li onClick={(e) => handleLiClick(i, e)} key={i}>{`li_${i}`}</li>
{/* <li onClick={handleLiClick.bind(this, i)}>{"s_" + i}</li> */}
</>
));
const handleLiClick = (i: number, e: any): void => alert(i);
// 条件渲染可以通过if或者&&运算符实现
const isShow: boolean = true;
let showButton;
if (!isShow) showButton = <button>显示按钮</button>;
else showButton = <a>不显示按钮</a>;
return (
<>
<span onClick={handleClick}>{msg}</span>
<ul>{demoList}</ul>
<div>
{isShow && <button>显示按钮</button>}
</div>
<div>
{showButton}
</div>
</>
);
};
export default Demo;
4. 组件间通讯 & props
没什么可说的,直接上代码吧,,
import React, { FC } from "react";
interface Props {
id: number;
msg: string;
onClick: () => void;
}
// 通过函数的参数传递
const Child: FC<Props> = ({ id, msg, onClick }) => {
const handleClick = () => {
onClick(id);
};
return (
<>
<span onClick={handleClick}>{`${id} - ${msg}`}</span>
</>
);
};
export default Child;
import React, { FC } from "react";
import Child from "./Child";
const Parent: FC = () => {
const emitClick = (v: number): void => alert(v);
const id = 12;
const msg = "props传递";
return <Child id={id} msg={msg} onClick={emitClick} />;
};
export default Parent;
5. Hook
useState()
const [state, setState] = useState(initialState)
// initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略,可以传入一个函数初始化复杂state
返回一个state
,以及更新state的函数,在初始渲染期间,返回的状态(state
)与传入的第一个参数(initialState
)值相同,setState
函数用于更新state
,它接收一个新的state
值并将组件的一次重新渲染加入队列,在后学的重新渲染中,useState
返回的第一个值始终是更新后最新的state
,如果新的state
需要通过使用前一个state
计算得出,那么可以讲函数传递给setState
,该函数将接收先前的state
,并返回一个更新后的值
const Times: FC = () => {
const [count, setCount] = useState(0);
// useState不会自动合并更新对象,可以使用展开运算符来达到合并更新对象的效果
const handleMerge = () => {
setCount(prev => {
return {...prev, ...updateValues}
})
}
return (
<>
Count: {count}
<button onClick = {setCount(count++)}>加一</button>
<button onClick = {setCount(c => c*2)}>乘二</button>
</>
)
}
useEffect()
useEffect(didUpdate, [...effect])
该Hook接收一个包含命令式、且可能有副作用代码的函数, 赋值给useEffect
的函数会在组件渲染到屏幕之后执行,默认情况下,effec
将在每轮渲染结束后,在一个延迟事件中被调用,这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,然而并不是所有的effect
都可以被延迟执行(一个对用户可见的DOM变更就必须在浏览器执行下一次绘制前被同步执行,这样用户才不会感觉到视觉上的不一致),虽然 useEffect
会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。在开始新的更新前,React 总会先清除上一轮渲染的effect
最常用的用法就是向服务端请求数据,代替class的生命周期
useEffect(
() => {
const subscription = props.source.subscribe();
/**
* @params Fuction callBack()
* effect可以返回一个函数,在组件被清除前调用,可以用来清除effec,e.g: 订阅,计时器
* 如果组件多次渲染,则在执行下一个effect之前,上一个effect就已经被清除
*/
return () => {
subscription.unsubscribe();
};
},
// 通过给useEffect传递第二个参数(数组值),来让函数仅在source变化时被调用
// 如果想只运行一次effect,可以传递一个空数组来实现
[props.source],
);
useContext()
const value = useContext(MyContext)
简单描述就是事件通讯机制,也有叫状态共享,在需要传值的父组件内注册Context
的provider
,在子组件进行消费,它接受一个context对象(React.createContext
的返回值)并返回该context
的当前值,当前的context值由上曾组件中距离当前组件最近的<MyContext.Provider>
的value prop
决定
export const UserContext = React.createContext('')
export default function Parent() {
return (
<>
<UserContext.Provider value={"hugo"}>
<Child></Child>
</UserContext.Provider>
</>
);
}
import {UserContext} from './Parent';
export default function Child(){
// 直接通过useContext获取传递过来的值
const userName = useContext(UserContext); // userName: hugo
return (
<>
// 通过Consumer标签来获取
<UserContext.Consumer>
{(n: string) => (
<div>props Name: {n}</div>
)}
</UserContext.Consumer>
</>
)
}
非基础Hooks
-
useReduce()
状态管理功能,
useState
的替代方法,redux
的简化版(不包含中间件和time travel
)// const [state, dispatch] = useReducer(reducer, initialArg, init) // 一个简单的计数器demo export const CountDemo: FC = () => { const [count, dispath] = useReducer((state, action) => { // state当前状态值, action dispath调用时传递的参数 switch(action){ case 'add': return state + 1; case 'subtract': return state - 1; // 返回新的状态值 -> count } // 初始化为0 }, 0) // 惰性初始化,传递一个初始化函数作为reducer的第三个参数 // 方便初始化移出,e.g: initial值为props时,方便修改重置 const init = (initial: number): number => initial; const [count1, dispatch1] = useReducer((s, a) => {}, 0, init) return ( <> <span>当前数量: {count}</span> <button onClick={() => dispatch('add')}>加</button> <button onClick={() => dispatch('subtract')}>减</button> </> ) }
-
useCallBack()/useMemo()
两个一起解释,直接给结论,作用时性能优化,并是不必须(非大型项目),只有在依赖变化时,才会调用穿进去的回调函数去重新计算结果,起到缓存的作用(e.g: 回调方法里返回的子组件不会因为其他state变动导致重新渲染)
// 返回的是一个函数 const memoizedCallback = useCallback(() => doSomething(...args), [...args]) // 返回的是一个值 e.g return <Component /> const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
-
useRef()
绑定一个可变的ref对象,其current属性保持最新的值,返回的 ref 对象在组件的整个生命周期内持续存在,举个例子: 在定时器中获取
state
后,再通过useState
更新值,定时器结束获取到的state
是更新前的值当 ref 对象内容发生变化时,
useRef
并不会通知你。变更.current
属性不会引发组件重新渲染,如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现const refContainer = useRef(initialValue); // 通过useRef实时获取最新的DOM节点 const domRef = useRef<HTMLInputElement>(null) <input ref={domRef} type="text" />
-
useLayoutEffect()
同
useEffect
, 但它会在所有的DOM变更之后同步调用effect
,可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect
内部的更新计划将被同步刷新。
6.附录 - 事件集合
事件类型 | 事件参考 | 对应属性 |
---|---|---|
剪贴板 Clipboard Events | onCopy | onCut | onPaste | clipboardData: DOMDataTransfer |
复合事件 Composition Events | onCompositionEnd | onCompositionStart | data: string |
键盘事件 Keyboard Events | onKeyDown | onKeyPress | onKeyup | charCode: number | key: string | keyCode: number | locale: string | location: number ... |
焦点事件 Focus Events | onFocus | onBlur | relatedTarget: DOMEventTarget |
表单事件 FormEvents | onChange | onInput | onInvalid | onReset |onSubmit | |
通用事件 | onError | onLoad | |
鼠标事件 | onClick | onMouseMover |onDrageOver ... | pageX: number | pageY: number | ctrlKey: boolean | button: number |clientX: number | clientY: number ... |
UI事件 | onScroll | detail: number | view: DOMAbstractView |
指针事件 | onPointDown | onPointMove | onPointOver ... | pointerId: number | width: number | height: number | pressure: number | tiltX: number | tiltY: number |
转载自:https://juejin.cn/post/7069634797127073799