irpas技术客

ReactNative中Hooks封装与使用_汉堡怪兽_react native hooks

网络 5295

组件是 React 代码复用的主要单元,但如何将一个组件封装的状态或行为共享给其他需要相同状态的组件并不是显而易见的。

ReactNative 和 React 一样可以使用函数式组件或 Class 组件。最开始只有 Class 组件能够使用 state ,函数式组件都是无状态的。并且渲染结果只与参数有关,参数相同,每次渲染结果都相同。

组件之间如果有复用的需求,有一些可复用的逻辑需要从组件中抽取出来,通常是使用 render props 或 高阶组件。

1.render props class Mouse extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ); } } class MouseTracker extends React.Component { render() { return ( <div> <h1>移动鼠标!</h1> <Mouse render={mouse => ( <Cat mouse={mouse} /> )}/> </div> ); } }

提供了一个 render 方法 让 能够动态决定什么需要渲染,而不是克隆 组件然后硬编码来解决特定的用例。

更具体地说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

2.高阶组件

高阶组件是参数为组件,返回值为新组件的函数。

function withSubscription(WrappedComponent, selectData) { return class extends React.Component { constructor(props) { super(props); this.state = { data: selectData(DataSource, props) }; } // 一些通用的逻辑处理 render() { // ... 并使用新数据渲染被包装的组件! return <WrappedComponent data={this.state.data} {...this.props} />; } }; }

这两种方案就是16.8版本之前的react所支持的组件间逻辑复用方案了,但是都有缺点,多逻辑复用容易出现嵌套问题。

3.Hooks

React Native 0.59 提供Hooks 的新特性,自从有了 React Hooks API,可以在不编写 class 的情况下以类组件的方式使用 state 以及其他的 React 特性

function Example() { // 声明一个叫 “count” 的 state 变量。 const [count, setCount] = useState(0); const { x, y } = useMouse(); return ( <View> <Text>You clicked {count} times</Text> <Button onClick={() => setCount(count + 1)}> Click me </Button> </View> ); }

可以利用 Hooks,将 ReactNative 组件打造成:任何事物的变化都是输入源,当这些源变化时会重新触发 ReactNative 组件的 render,你只需要挑选组件绑定哪些数据源(use 哪些 Hooks),然后只管写 render 函数就行了!

与函数组件相比,类组件缺点:

代码重用:在hooks出来之前,常见的代码重用方式是HOCs和render props,这两种方式带来的问题是:你需要解构自己的组件,非常的笨重,同时会带来很深的组件嵌套复杂的组件逻辑:在class组件中,有许多的 lifecycle 函数,你需要在各个函数的里面去做对应的事情。这种方式带来的痛点是:逻辑分散在各处,开发者去维护这些代码会分散自己的精力,理解代码逻辑也很吃力 3.1 需要遵循两条规则

1.只在最顶层使用 Hook

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们,确保 Hook 在每一次渲染中都按照同样的顺序被调用。

2.只在 React 函数中调用 Hook

在 React 的函数组件中调用 Hook在自定义 Hook 中调用其他 Hook不要在普通的 JavaScript 函数中调用 Hook

App中可以使用 eslint-plugin-react-hooks 的 ESLint 插件来强制执行这两条规则。

3.2 基础Hooks useState useEffect useContext useReducer useCallback useMemo useRef useImperativeHandle useLayoutEffect useDebugValue 3.2.1 常用基础Hooks

1.useState

useState让开发者能够在函数组件里面拥有state,修改state。

const [state, setState] = useState(initialState);

2.useEffect

使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。

useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], );

3.useRef

可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式,返回的 ref 对象在组件的整个生命周期内持续存在。

与useState区别:变更 .current 属性不会引发组件重新渲染,而useState值的更新会触发组件重新渲染

3.2.2 常用三方库的Hooks API

Redux Hooks API

const dispatch = useDispatch() const loadingEffect = useSelector(state =>state.loading)

React Navigation

useNavigation useRoute useNavigationState useFocusEffect ... 3.3 函数组件需特殊注意的地方

ReactNative 或者 React 可以通过减少非必要的 render 来优化性能,在使用类组件的时候,使用的 React 优化 API 主要是:shouldComponentUpdate 和 PureComponent

而使用函数组件时,除了需要减少非必要render之外,每次 render 都会重新从头开始执行函数,因此还需要注意减少重复的复杂计算或逻辑处理

1.React.memo

这个 API 可以说是对标类组件里面的 PureComponent,这是可以减少重新 render 的次数的。

function Child(props) { return <Text>{props.name}</Text> } export default React.memo(Child)

把声明的组件通过React.memo包一层就好了,React.memo其实是一个高阶函数,传递一个组件进去,返回一个可以记忆的组件。

默认情况下其只会对 props 的复杂对象做浅比较,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。

2.useCallback

这个api也可以减少重新 render 的次数

function App() { const [title, setTitle] = useState("这是一个 title"); const callback = () => { setTitle("标题改变了"); }; return ( <View> <Text>{title}</Text> <Child onClick={callback} name="桃桃" /> </View> ); }

在函数式组件里每次重新渲染,函数组件都会重头开始重新执行,那么这两次创建的 callback 函数肯定发生了改变,所以导致了子组件重新渲染。

// 通过 useCallback 进行记忆 callback,并将记忆的 callback 传递给 Child const memoizedCallback = useCallback(()=>{setTitle("标题改变了")}, []) return ( <View> <Text>{title}</Text> <Child onClick={memoizedCallback} name="桃桃" /> </View> ); }

3.useMemo

useMemo 的使用场景主要是用来缓存计算量比较大的函数结果,可以避免不必要的重复计算

function App() { const [count, setCount] = useState(1); function expensiveFn() { let result = 0; for (let i = 0; i < 10000; i++) { result += i; } return result; } const result = useMemo(expensiveFn, []); //const result = expensiveFn(); return ( <View > <Text>count:{count}</Text> <Text>result:{result}</Text> <Button onClick={() => setCount(count + 1)}>count+1</Button> </View> ); } 3.4 自定义Hooks

除了React Hooks提供的基础hook,业务开发中还会有许多常用的基础功能或者可以复用的逻辑,这些也可以抽离成自定义的hooks来实现复用。

3.5 使用Hooks封装的对比

Class组件

class CrmCreat extends Component { constructor(props) { super(props) this.state = { } } componentDidMount() {} addItem = () => { } showRegionDialog = () => { } showShadow = () => { } hideShadow = () => { } disable = () => {} submit = () => { } check() {} checkCompanyName() { } searchCompany = name => {} renderModal = () => {} render() { } }

使用Hooks封装的函数组件

export default props => { const { navigation } = props const { qccQuery, crmEdit, crmAdd, checkName } = ajaxStore.crm const modal = useRef(null) const [state, setState] = useMergeState({}) const { show, shadow } = useRegionDialog({}) const { doFetch: _qccQuery } = useAsync({}) const { doFetch: _crmEdit } = useAsync({}) const { doFetch: _crmAdd } = useAsync({}) const { data: isChecked, doFetch: _checkName } = useAsync({}) const timer = useDebounce(name => {}, 500) const { check } = useFormValid({}) const addItem = () => { } const checkPerson = () => { } const checkCompanyName = async () => {} const renderModal = () => {} return (<View></View>) }


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #React #Native #hooks #组件是 #reactnative #