简介
- 设计理念
单向数据流、虚拟 DOM、组件化 - 组件化编程的思想
React 以组件的方式去重新思考用户界面的构成,将用户界面上每一个功能相对独立的模块定义成组件,然后将小组件通过组合或嵌套的方式构成大组件,最终完成整体 UI 的构建。
项目启动
npx create-react-app react-project --template typescript
CSS 模组
-
直接引入整个css文件
import './index.css' 使用:<div className="app" />
-
JSS 模块化引入组件
npm install typescript-plugin-css-modules --save-dev import styles from './index.module.css' 使用:<div className={styles .app} />
state 和 props 的区别
- state 是组件对内的接口,props 是组件对外的接口
- state 用于组件内部的数据传递,props 用于数组间数据传递
- state:
- state 是私有的,可以认为 state 是组件的“私有属性”,用 setState() 来修改。若直接修改 state,组件不会触发 render 函数,页面不会渲染。
- 构建函数 constructor 是唯一可以初始化 state 的地方。调用 setState 后,state 不会立刻更新,是异步操作;不要依赖当前的 state 来计算下一个 state。
- props(Properties 的缩写):
- 本质上说,props 是传入函数的参数,是传入组件内部的数据,是从父组件传递到子组件的数据。
- props 是只读属性
React Event 事件处理机制
如 onClick
等
为了少输入代码,同时为了避免 this 造成的困扰,我们在这里使用箭头函数来进行事件处理。
handleClick = () => {
this.setState({
count: this.state.count + 1
}, () => {
// 数据同步
console.log('count', this.state.count)
})
// 数据不同步
console.log('click', this.state.count)
// 说明 setState 是异步更新,同步执行的
}
render() {
const { count } = this.state
return (
<div>
<button onClick={this.handleClick}>{count}</button>
</div>
)
}
生命周期
生命周期可分为三个阶段:
图片
-
Mounting:初始化阶段,创建虚拟 DOM,渲染 UI
constructor:初始化组件 state
componentDidMount:在组件创建好 dom 元素以后,挂载进页面时调用,一般用来获取网络数据 -
Updating:更新阶段,更新虚拟 DOM,重新渲染 UI
componentWillReceiveProps:在组件接收到一个新的 prop(更新后)时被调用,但是会产生副作用,已被废弃,可使用 getDerivedStateFromProps 来进行替代。/* * getDerivedStateFromProps:将传入的props映射到state上面。这个函数会在每次 re-rendering 之前被调用,这意味着即使你的 props 没有任何变化,而是父 state 发生了变化,导致子组件发生了 re-render ,这个生命周期函数依然会被调用。 * getDerivedStateFromProps 是一个静态函数,也就是这个函数不能通过 this 访问到 class 的属性,也并不推荐直接访问属性。而是应该通过参数提供的 nextProps 以及 prevState 来进行判断,根据新传入的 props 来映射到 state。 * 如果 props 传入的内容不需要影响到你的 state,那么就需要返回一个 null,这个返回值是必须的。 */ static getDerivedStateFromProps(nextProps, prevState) { const {type} = nextProps; // 当传入的type发生变化的时候,更新state if (type !== prevState.type) { return { type } } // 否则,对于state不进行任何操作 return null }
shouldComponentUpdate:使用 shouldComponentUpdate() 以让 React 知道当前状态或属性的改变是否不影响组件的输出,默认返回 ture ,返回 false 时不会重写 render,该方法并不会在初始化渲染或当使用 forceUpdate() 时被调用
shouldComponentUpdate(nextProps, nextState) { return true }
componentDidUpdate:组件更新后调用
-
Unmounting:删除虚拟 DOM,移除 UI
componentWillUnmount:组件将要销毁时调用,可用来回收内存、移除监听或清空定时器等
获取网络 API 数据
对 any 的理解:
- 资源来源于网络请求,返回的数据类型不受控制
- 前端强行定义 API 数据类型,违法前后端分离的原则
- 不能为了使用 Type 而放弃 JavaScript 的灵活性
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => {
this.setState({
robotGallery: data
})
})
}
React 17 新版本
- 虽然没有新功能,但具有战略意义
- 替换了底层的代码,向下兼容,同时甩掉了历史包袱
- 没有代码断层,给未来留下了足够的升级空间
- 逐步升级方案:允许多个 react 版本共存,如先升级路由系统、再升级弹窗,平滑过渡到最新版本
- breaking changes:
事件委托机制改变
向原生浏览器靠拢
删除事件池
useEffect 清理操作改为异步操作
JSX 不可返回 undefined
删除部分私有 API - 展望:v17 不是过渡版本,而是承上启下的战略版本,未来将会更加强调函数事组件,支持微前端等
钩子 hooks 简介
- 消息处理的一种方法,用来监视指定程序
- 函数组件中需要处理副作用,可以用钩子把外部代码“钩”进来
- hooks 一律使用 use 前缀命名:useXxx
- 常用钩子:useState、useEffect、useContext、useReducer、useLayoutEffect、useDebugValue
- hooks 的本质:一类特殊的函数,为你的函数式组件(functional Component)注入特殊的功能
- hooks 的目的是为了给函数式组件加上状态
钩子 hooks 的使用
-
useState:
// useState 的参数为 count 的初始值,setCount 用于 count 的更新 const [ count, setCount ] = useState<number>(0) const [ robotGallery, setRobotGallery ] = useState<any[]>([]) <button onClick={() => setCount(count + 1)}>count:{count}</button>
-
useEffect、loading、请求异常
const [ loading, setLoading ] = useState<boolean>(false) const [ errMsg, setErrMsg ] = useState<string>('') /* * 一个组件内可以有多个useEffect * 若第二个参数不传,则在每次页面渲染UI后调用 * 若第二个参数为空数组,则等同于 componentDidMount * 若第二个参数为非空数组,数组内参数变化后调用 */ useEffect(() => { console.log(count) }, [count]) useEffect(() => { const getRobots = async () => { setLoading(true) setErrMsg('') try { const res = await fetch('https://jsonplaceholder.typicode.com/users') if (res.status === 200) { const data = await res.json() setRobotGallery(data) } else { setErrMsg(res.status + '') } } catch (error) { setErrMsg(error) } setLoading(false) } getRobots() }, []) { errMsg && errMsg.trim() && <h4>{errMsg.trim()}</h4>} { loading ? <h3>正在加载</h3> : <div className={styles['robot-layer']}> { robotGallery.map(item => <Robot key={item.id} id={item.id} name={item.name} phone={item.phone} email={item.email} />) } </div> }
-
父组件向子组件传递数据(3种)
第一种:props// index.tsx <App usename="张三" /> // App.tsx <h2>{props.usename}</h2>
第二种:appContext.Consumer
// index.tsx const defauleContextValue = { gender: '男' } export const appContext = React.createContext(defauleContextValue) // 孙子组件:Robot.tsx import { appContext } from '../index' const Robot: React.FC<RobotProps> = ({ id, name, phone, email }) => { return ( <appContext.Consumer> { value => { return ( <div className={styles['robot-box']}> <img src={`https://robohash.org/${id}`} alt="robot"/> <p>name:{name}</p> <p>phone:{phone}</p> <p>email:{email}</p> <p>gender:{value.gender}</p> </div> ) } } </appContext.Consumer> ) }
第三种:useContext
// 孙子组件:Robot.tsx import { appContext } from '../index' const Robot: React.FC<RobotProps> = ({ id, name, phone, email }) => { const value = useContext(appContext) return ( <div className={styles['robot-box']}> <img src={`https://robohash.org/${id}`} alt="robot"/> <p>name:{name}</p> <p>phone:{phone}</p> <p>email:{email}</p> <p>gender:{value.gender}</p> </div> ) }
-
组件化 context provider
创建 AppState.tsx 文件import React, { useState } from 'react'; interface AppStateValue { gender: string ShoppingCart: { items: { id: number, name: string, count: number }[] } } const defauleContextValue: AppStateValue = { gender: '男', ShoppingCart: { items: [] } } export const appContext = React.createContext(defauleContextValue) export const appSetStateContext = React.createContext< React.Dispatch<React.SetStateAction<AppStateValue>> | undefined >(undefined) export const AppStateProvider: React.FC = props => { const [ state, setState ] = useState(defauleContextValue) return ( <appContext.Provider value={state}> <appSetStateContext.Provider value={setState}> {props.children} </appSetStateContext.Provider> </appContext.Provider> ) }
index.tsx
import { AppStateProvider } from './AppState' ReactDOM.render( <React.StrictMode> <AppStateProvider> <App /> </AppStateProvider> </React.StrictMode>, document.getElementById('root') );
子组件,变更全局状态
import { appContext, appSetStateContext } from '../AppState' const value = useContext(appContext) const setState = useContext(appSetStateContext) const addToCart = () => { if (setState) { setState(state => { const items = JSON.parse(JSON.stringify(state.ShoppingCart.items)) const itemIndex = items.findIndex(obj => obj.id === id) if (itemIndex !== -1) { items[itemIndex].count++ return { ...state, ShoppingCart: { items: [...items] } } } return { ...state, ShoppingCart: { items: [...items, { id, name, count: 1 }] } } }) } } <button onClick={addToCart}>加入购物车</button>
-
高阶组件(HOC:高阶组件并不是组件,而是接受组件作为参数,并且返回组件的函数):withAddToCart
AddToCart.tsximport React, { useContext } from 'react' import { appSetStateContext } from '../AppState' import { RobotProps } from './Robot' export const withAddToCart = (ChildComponent: React.ComponentType<RobotProps>) => { return props => { const setState = useContext(appSetStateContext) const addToCart = (id, name) => { if (setState) { setState(state => { const items = JSON.parse(JSON.stringify(state.ShoppingCart.items)) const itemIndex = items.findIndex(obj => obj.id === id) if (itemIndex !== -1) { items[itemIndex].count++ return { ...state, ShoppingCart: { items: [...items] } } } return { ...state, ShoppingCart: { items: [...items, { id, name, count: 1 }] } } }) } } return <ChildComponent {...props} addToCart={addToCart} /> } }
robot.tsx
import React, { useContext } from 'react' import { appContext } from '../AppState' import { withAddToCart } from './AddToCart' export interface RobotProps { id: number name: string email: string phone: string addToCart: (id, name) => void } const Robot: React.FC<RobotProps> = ({ id, name, phone, email, addToCart }) => { const value = useContext(appContext) return ( <div> <img src={`https://robohash.org/${id}`} alt="robot"/> <p>name:{name}</p> <p>phone:{phone}</p> <p>email:{email}</p> <p>gender:{value.gender}</p> <p><button onClick={() => addToCart(id, name)}>加入购物车</button></p> </div> ) } export default withAddToCart(Robot)
-
自定义 hooks:useAddToCart
export const useAddToCart = () => { const setState = useContext(appSetStateContext) const addToCart = (id, name) => { if (setState) { setState(state => { const items = JSON.parse(JSON.stringify(state.ShoppingCart.items)) const itemIndex = items.findIndex(obj => obj.id === id) if (itemIndex !== -1) { items[itemIndex].count++ return { ...state, ShoppingCart: { items: [...items] } } } return { ...state, ShoppingCart: { items: [...items, { id, name, count: 1 }] } } }) } } return addToCart }
robot.tsx
const addToCart = useAddToCart()
React 中,Component、Element、Instance 的区别?
- 一个元素是一个普通对象,他描述一个组件实例和它所要求的的属性,或者一个 dom 节点和它所要求的属性
- 组件封装了元素树,组件可以是类或者函数
- 实例是 Component class 中 this 所指向的东西,即组件实例。Functional Component 没有实例
网友评论