Open py-tofee opened 3 years ago
<button onClick={handleClick}></button>
function handleClick(e) { // react中的e是合成事件,不需要我们担心跨浏览器兼容的问题 e.preventDefault() //... }
class MyComponent extends React.Component { constructor(props) { super(props) this.state = { isToggleOn: true } // 方法一:手动绑定 this.handleClick = this.handleClick.bind(this) } // class中的方法默认不会绑定this handleClick() { this.setState((state) => ({ isToggleOn: !state.isToggleOn })) } // 方法二:使用public class fields语法 handleClick = () => { this.setState((state) => ({ isToggleOn: !state.isToggleOn })) } render() { return ( // 如果直接使用没有绑定this的handleClick,handleClick中的this为undefined <button onClick={this.handleClick}></button> // 方法三:此方法在每次渲染(调用render函数)的时候,都会创建一个新的函数,如果被当做prop传给子组件,子组件可能会进行额外的渲染,容易导致性能问题 <myButton onClick={() => this.handleClick()}></myButton> ) } }
// 事件对象e通常作为函数的第二个参数传递 // 箭头函数中的e需要显示的传递 <button onClick={(e) => this.handleClick(id, e)}></button> // bind方式,e被隐式的传递,不应该在jsx中使用bind,bind也会返回一个新函数 <button onClick={this.handleClick.bind(this, id)}></button>
<input type="text" value={this.state.value} onChange={this.handleChange} /> <select value={this.state.value} onChange={this.handleChange}> <option>...</option> </select> // 多选,value为数组 <select multiple={true} value={['a', 'b']} onChange={this.handleChange}> <option>...</option> </select>
// 允许用户从本地选择一个或者多个文件,value是只读的,且只能有用户主动发起读取文件的行为 <input type="file" />
function Page(props) { const userLink = ( <Link href={user.permalink}> <Avatar user={props.user} size={props.avatarSize} /> </Link> ) return <PageLayout userLink={userLink} /> } // 现在,我们有这样的组件: <Page user={user} avatarSize={avatarSize} /> // ... 渲染出 ... <PageLayout userLink={...} /> // ... 渲染出 ... <NavigationBar userLink={...} /> // ... 渲染出 ... {props.userLink}
向当前组件树下的所有子组件“广播”需要共享的数据,例如local, theme或者一些缓存的用户信息等
Context.Provider
// 只有当所处组件树中没有匹配到Provider时,defaultValue才会生效 const MyContext = React.createContext(defaultValue); // 每个context对象都会返回一个Provider React组件,它允许消费组件订阅context的变化 // 一个Provider组件可以对应多个消费组件,多个Provider组件可以嵌套使用,里层的数据会覆盖外层的数据 // 当Provider的value值发生变化,它内部的所有消费组件都会重新渲染,且不受shouldComponentUpdate函数的影响 <MyContext.Provider value={/* 某个值 */}>
Context.Consumer - class
class MyClass extends React.Component { // class的静态属性contextType会被赋值为一个Context对象 // 组件内部可以通过this.context来消费最近Context对象上的值 // this.context可以在任意生命周期中访问到,render函数中也能访问到 static contextType = MyContext; render() { let value = this.context; /* 基于这个值进行渲染工作 */ } }
Context.Consumer - functional
function MyClass() { return (<MyContext.Consumer> {value => { // 基于context的值进行渲染 }} </MyContext.Consumer>) }
Context.displayName 定义在DevTools中如何显示创建的context
const MyContext = React.createContext(defaultValue) MyContext.displayName = 'MyDisplayName'
// 在DevTools中显示为 MyDisplayName.Provider // 在DevTools中显示为 MyDisplayName.Consumer
5. 动态Context ```js toggleTheme() { // 修改state.theme } <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext.Provider>
也可以将更新context的函数放在context中,Consumer组件可以直接调用Provider传进来的context中的函数,更新context的值, 这样嵌套层级深的组件也可以方便的修改context值
export const ThemeContext = React.createContext({ theme: themes.dark, toggleTheme: () => {/*...*/} })
嵌套使用Provider和Consumer,可以实现一个组件消费多个context
如果一个class组件中,定义了static getDirevedStateFromError()或者componentDidCatch中的任意一个或者两个,那么它就变成一个 错误边界组件,错误边界组件只能捕获其子组件的错误,不能捕获自身的错误;错误边界组件可以嵌套使用,如果一个错误边界组件无法渲染 错误信息,那么它会冒泡至最近的上层错误边界组件去处理。 错误边界只能处理渲染期间的错误,不能捕获事件处理器内部的错误,因为事件处理器不会在渲染期间触发,比如点击事件等。
class ErrorBoundary extends React.Component { constructor(props) { super(props) this.state = { hasError: false } } static getDirevedStateFromError(error) { // 更新 state 使下一次渲染能够显示降级后的UI return { hasError: true } } componentDidCatch(error, errorInfo) { // 可以在这里将错误日志上报给服务器 logErrorToService(error, errorInfo) } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1> } return this.props.childen } } // 使用 <ErrorBoundary> <MyWidget /> </ErrorBoundary>
在开发环境中,React 16会把渲染期间发生的所有错误打印到控制台,可以定位到具体某个组件(文件名)的错误, 在babel配置中添加插件 babel-plugin-transform-react-jsx-source,可以定位到具体行号的错误信息 使用create-react-app创建,默认是开启错误信息可追踪到文件名和行号的。
React ref 用于实现对组件实例的引用 或者 指向具体的DOM元素 使用React.createRef()创建React ref,只有React.forwardRef()定义的组件才能接受ref参数, 普通函数组件和class组件 不接受 ref参数
const FancyButton = React.forwardRef((props, ref) => ( // ref转发到DOM元素上 <button ref={ref} className="fancy-button">{props.children}</button> ))
父组件
const ref = React.createRef() // 向下传递ref <FancyButton ref={ref}>click me</FancyButton>
高阶组件:参数为组件,返回值为新组件的函数
function logProps(wrappedComponent) { class LogProps extends React.Component { constructor(props) { super(props) } render() { const {forwardedRef, ...rest} = this.props return <wrappedComponent ref={forwardedRef} {...rest}></wrappedComponent> } } return React.forwardRef((props, ref) => { // 将ref作为props属性forwardedRef传递下去 return <LogProps {...props} forwardedRef={ref} /> }) } // FancyButton.js class FancyButton extends React.Component { // ... } export default logProps(FancyButton) // 父组件 import FancyButton from './FancyButton' const ref = React.createRef() <FancyButton ref={ref} label="click me" handleClick={handleClick} />
ref作用于HTML元素时,ref.current指向DOM元素
ref作用于自定义class组件时,ref.current指向组件的挂载实例
ref不能直接作用于函数式组件,因为函数式组件没有实例,需要配合React.forwardRef使用
回调Refs 可以将一个函数绑定到ref上,挂载的时候会将DOM或者组件实例当做函数的参数,在函数中可以保存ref值
类似vue中的 angular中的,react中使用将元素分组,而无需向DOM添加额外的元素。 key是唯一可以传递给Fragment的属性。
// 用在循环中时,可以在React.Fragment上指定key属性 <React.Fragment key={key}> <li></li> <li></li> <li></li> </React.Fragment> // 短语法-不支持key属性 <> <li></li> <li></li> <li></li> </>
高阶组件:参数为组件,返回值为新组件的函数 高阶组件函数应该是一个纯函数,没有任何副作用,不应该对传入的组件做任何修改 传入的组件应该是被包裹在新组件的render函数中(组件组合) 不应该在render函数中调用HOC函数,容易导致组件更新时,丢失子组件的状态
使用HOC返回的被包装的新组件,会丢失原组件的静态方法,需要复制静态方法到新组件上,手动复制需要知道具体有哪些方法 可以使用hoist-non-react-statics自动拷贝所有原组件的静态方法
<MyComponent msg={'world'} count={1+2} name="hello" />
const props = {name: 'hello', msg: 'world', coung: 3}; <MyComponent {...props} />
<div> {isShow && <Content />} <Footer /> </div>
定义:组件接收一个函数prop,该函数用于告知组件需要渲染什么内容,这种技术称为"Render Props"
import PropTypes from 'prop-types'; class MyComponent extends React.Component { // 设置默认值-方法二 需要配合Bable转换工具`transform-class-properties`使用 static defaultProps = { name: 'stranger' } } // 设置默认值-方法一 MyComponent.defaultProps = { name: 'stranger' } MyComponent.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number }
// 受控组件使用value设置默认值,且value会控制组件的后续更新 // 非受控组件使用defaultValue设置默认值,这样react不会控制非受控组件的更新 render() { defaultVal = '' return ( <form> <input value={defaultVal} /> <input defaultValue={defaultVal} /> </form> ) }
class MyComponent extends React.Component { constructor(props) { // 构造函数中必须先调用父类构造函数super(),然后才可以在构造函数中访问this super(props) this.state = { // ... } } } class MyComponent extends React.Component { // state = { // ... } }
在渲染之前,对props和state进行浅层比较,跳过不必要的更新
即将过时,避免使用
即将过时,避免使用,以下方法可能在一次更新中被调用多次
卸载阶段
错误处理 渲染过程中,生命周期,或子组件的构造函数中抛出错误时,会调用以下方法
强制调用组件的render方法,跳过shouldComponentUpdate函数,但是不影响子组件的shouldComponentUpdate
React.memo
React.memo(MyComponent, areEqualFn)
动态加载组件 React.Suspense包裹需要动态加载的组件,可以在等待lazy组件加载时做优雅降级,如loading指示器等
const OtherComponent = React.lazy(() => import('./OtherComponent')) <React.Suspense fallback={<div>Loading...</div>}> <OtherComponent></OtherComponent> </React.Suspense>
事件处理
条件渲染
列表 & key
表单
状态提升
组合
Context 组件之间共享
向当前组件树下的所有子组件“广播”需要共享的数据,例如local, theme或者一些缓存的用户信息等
Context.Provider
Context.Consumer - class
Context.Consumer - functional
Context.displayName 定义在DevTools中如何显示创建的context
也可以将更新context的函数放在context中,Consumer组件可以直接调用Provider传进来的context中的函数,更新context的值, 这样嵌套层级深的组件也可以方便的修改context值
嵌套使用Provider和Consumer,可以实现一个组件消费多个context
错误边界组件
如果一个class组件中,定义了static getDirevedStateFromError()或者componentDidCatch中的任意一个或者两个,那么它就变成一个 错误边界组件,错误边界组件只能捕获其子组件的错误,不能捕获自身的错误;错误边界组件可以嵌套使用,如果一个错误边界组件无法渲染 错误信息,那么它会冒泡至最近的上层错误边界组件去处理。 错误边界只能处理渲染期间的错误,不能捕获事件处理器内部的错误,因为事件处理器不会在渲染期间触发,比如点击事件等。
错误追踪
在开发环境中,React 16会把渲染期间发生的所有错误打印到控制台,可以定位到具体某个组件(文件名)的错误, 在babel配置中添加插件 babel-plugin-transform-react-jsx-source,可以定位到具体行号的错误信息 使用create-react-app创建,默认是开启错误信息可追踪到文件名和行号的。
Refs转发
React ref 用于实现对组件实例的引用 或者 指向具体的DOM元素 使用React.createRef()创建React ref,只有React.forwardRef()定义的组件才能接受ref参数, 普通函数组件和class组件 不接受 ref参数
父组件
在高阶组件中转发ref
高阶组件:参数为组件,返回值为新组件的函数
ref
ref作用于HTML元素时,ref.current指向DOM元素
ref作用于自定义class组件时,ref.current指向组件的挂载实例
ref不能直接作用于函数式组件,因为函数式组件没有实例,需要配合React.forwardRef使用
回调Refs 可以将一个函数绑定到ref上,挂载的时候会将DOM或者组件实例当做函数的参数,在函数中可以保存ref值
Fragments
类似vue中的 angular中的,react中使用将元素分组,而无需向DOM添加额外的元素。
key是唯一可以传递给Fragment的属性。
高阶组件HOC
高阶组件:参数为组件,返回值为新组件的函数 高阶组件函数应该是一个纯函数,没有任何副作用,不应该对传入的组件做任何修改 传入的组件应该是被包裹在新组件的render函数中(组件组合) 不应该在render函数中调用HOC函数,容易导致组件更新时,丢失子组件的状态
使用HOC返回的被包装的新组件,会丢失原组件的静态方法,需要复制静态方法到新组件上,手动复制需要知道具体有哪些方法 可以使用hoist-non-react-statics自动拷贝所有原组件的静态方法
JSX
JSX 中 props
JSX 中 子元素
Render Props
定义:组件接收一个函数prop,该函数用于告知组件需要渲染什么内容,这种技术称为"Render Props"
PropTypes 类型检查
处理表单数据
React State 初始化两种方法
React内置的PureComponent组件
在渲染之前,对props和state进行浅层比较,跳过不必要的更新
React.Component
生命周期
即将过时,避免使用
即将过时,避免使用,以下方法可能在一次更新中被调用多次
卸载阶段
错误处理 渲染过程中,生命周期,或子组件的构造函数中抛出错误时,会调用以下方法
不常用生命周期方法
shouldComponentUpdate(nextProps, nextState): boolean
static getDerivedStateFromProps(props, state)
getSnapshotBeforeUpdate(prevProps, prevState)
第三方库
react组件中可以调用的方法只有下面两个,生命周期是react主动调用的
setState(updater, [callback])
forceUpdate(callback)
强制调用组件的render方法,跳过shouldComponentUpdate函数,但是不影响子组件的shouldComponentUpdate
React.memo
React.memo
中React.memo
仅检查props的变更,默认进行浅比较,也可以自定义比较函数React.memo(MyComponent, areEqualFn)
React.Children
React.Children.map(children, function[(thisArg)])
React.Children.forEach(children, function[(thisArg)])
React.Children.count(children)
React.Children.only(children)
React.Children.toArray(children) 返回扁平数组,每项增加key值
React.lazy, React.Suspense
动态加载组件 React.Suspense包裹需要动态加载的组件,可以在等待lazy组件加载时做优雅降级,如loading指示器等
ReactDOM