当前位置:网站首页 > React Native移动开发 > 正文

2.React组件化

课堂目标

  • 掌握组件化开发中多种实现技术
  • 了解组件化概念,能设计并实现⾃⼰需要的组件
  • 掌握使⽤跨层级通信-Context(新API在v>=16.3)
  • 组件复合 - Composition
  • ⾼阶组件 - HOC
  • Hooks(>=16.8)
  • 掌握第三⽅组件的使⽤

知识要点

  • 运⽤Context
  • 运⽤组件复合 - Composition
  • 运⽤⾼阶组件 - HOC
  • Hooks使⽤
  • 使⽤umi antD

资源

  • Context参考
  • HOC参考
  • Hooks参考
  • antD参考

antd-pro安装参考:https://pro.ant.design/docs/getting-startedcn#%E5%AE%89%E8%A3%85

  • umi参考

起步

组件化优点:

  • 增强代码重⽤性,提⾼开发效率
  • 简化调试步骤,提升整个项⽬的可维护性
  • 便于协同开发

快速开始

Create React App 中文文档

npx create-react-app my-app cd my-app npm start

组件跨层级通信 - Context

React中使⽤Context实现祖代组件向后代组件跨层级传值。Vue中的provide & inject来源于Context

在Context模式下有两个⻆⾊

  • Provider:外层提供数据的组件
  • Consumer :内层获取数据的组件

使⽤Context

创建Context => 获取Provider和Consumer => Provider提供值 =>Consumer消费值

范例:模拟redux存放全局状态,在组件间共享

//App.js import React from 'react'; import Home from './pages/Home' import User from './pages/User' import { Provider } from './AppContext' //引⼊Context的Provider const store = { home: { imgs: [{ "src": "//m.360buyimg.com/mobilecms/s700x280_jfs/t1/49973/2/8672//5dEcd46f8e7/0669f8801dff67e8.jpg!cr_1125x445_0_171!q70.jpg.dpg" }] }, user: { isLogin: true, userName: "true" } } function App() { return ( <div classname="app"> <provider value="{store}"> <home /> </provider> </div> ); } export default App;

AppContext.js

import React, { Component } from 'react' export const Context = React.createContext() export const Provider = Context.Provider export const Consumer = Context.Consumer

/pages/Home.js

import React, { Component } from 'react' import { Consumer } from '../AppContext' export default class Home extends Component { render() { return <Consumer>{(ctx) => <HomeCmp {...ctx} />}</Consumer> } } function HomeCmp(props) { const { home, user } = props const { isLogin, userName } = user return <div>{isLogin ? userName : '登录'}</div> }

/pages/User.js

import React, { Component } from 'react' import { Consumer } from '../AppContext' import TabBar from '../components/TabBar' export default class User extends Component { render() { return ( <> <Consumer>{(ctx) => <UserCmp {...ctx} />}</Consumer> <TabBar /> </> ) } } function UserCmp(props) { const { home, user } = props const { isLogin, userName } = user return <div>{isLogin ? userName : '登录'}</div> }

/components/TabBar

import React from 'react' import { Consumer } from '../AppContext' export default function TabBar() { return ( <div> <Consumer>{(ctx) => <TabBarCmp {...ctx} />}</Consumer> </div> ) } function TabBarCmp(props) { const { home, user } = props const { isLogin, userName } = user return <div>{isLogin ? userName : '登录'}</div> }

在React的官⽅⽂档中,Context被归类为⾼级部分(Advanced),属于React的⾼级API,但官⽅并不建议在稳定版的App中使⽤Context。

不过,这并⾮意味着我们不需要关注Context。事实上,很多优秀的React组件都通过Context来完成⾃⼰的功能,⽐如react-redux的<Provider />,就是通过Context提供⼀个全局态的store,拖拽组件react-dnd,通过Context在组件中分发DOM的Drag和Drop事件,路由组件react-router通过Context管理路由状态等等。在React组件开发中,如果⽤好Context,可以让你的组件变得强⼤,⽽且灵活。

函数组件中可以通过useContext引⼊上下⽂,后⾯hooks部分介绍

Composition-组件复合

复合组件给与你⾜够的敏捷去定义⾃定义组件的外观和⾏为,这种⽅式更明确和安全。如果组件间有公⽤的⾮UI逻辑,将它们抽取为JS模块导⼊使⽤⽽不是继承它。

Composition-基本使⽤

/pages/Layout.js【不具名】

import React, { Component } from 'react' export default class Layout extends Component { componentDidMount() { const { title = '商城' } = this.props document.title = title } render() { const { children, title = '商城' } = this.props return ( <div style={ 
  { background: 'yellow' }}> <p>{title}</p> {children.btns ? children.btns : children} <TabBar /> </div> ) } } function TabBar(props) { return <div>TabBar</div> }

/pages/Home.js

import React, { Component } from 'react' import { Consumer } from '../AppContext' import Layout from './Layout' export default class Home extends Component { render() { return <Consumer>{(ctx) => <HomeCmp {...ctx} />}</Consumer> } } function HomeCmp(props) { const { home, user } = props const { carsouel = [] } = home const { isLogin, userName } = user return ( <Layout title="⾸⻚"> <div> <div>{isLogin ? userName : '未登录'}</div> {carsouel.map((item, index) => { return <img key={'img' + index} src={item.img} /> })} </div> </Layout> ) }

/pages/User.js【传个对象进去就是具名插槽】

import React, { Component } from 'react' import { Consumer } from '../AppContext' import Layout from './Layout' export default class User extends Component { render() { return ( <div> <p>⽤户中⼼</p> <Consumer>{(ctx) => <UserCmp {...ctx} />}</Consumer> </div> ) } } function UserCmp(props) { const { home, user } = props const { carsouel = [] } = home const { isLogin, userName } = user return ( <Layout title="⽤户中⼼"> { 
  { btns: <button>下载</button>, }} {/* <div> <div>⽤户名: {isLogin ? userName : '未登录'} </div> </div> */} </Layout> ) }

实现⼀个简单的复合组件,如antD的Card

import React, { Component } from 'react' function Card(props) { return <div className="card">{props.children}</div> } function Formbutton(props) { return ( <div className="Formbutton"> <button onClick={props.children.defaultBtns.searchClick}> 默认查询 </button> <button onClick={props.children.defaultBtns.resetClick}> 默认重 置 </button> {props.children.btns.map((item, index) => { return ( <button key={'btn' + index} onClick={item.onClick}> {item.title} </button> ) })} </div> ) } export default class CompositionPage extends Component { render() { return ( <div> <Card> <p>我是内容</p> </Card> CompositionPage <Card> <p>我是内容2</p> </Card> <Formbutton> { 
  { /* btns: ( <> <button onClick={() =>console.log('enn')}>查询</button> <button onClick={() =>console.log('enn2')}>查询2</button> </> ) */ defaultBtns: { searchClick: () => console.log('默认查询'), resetClick: () => console.log('默认重置'), }, btns: [ { title: '查询', onClick: () => console.log('查询'), }, { title: '重置', onClick: () => console.log('重置'), }, ], }} </Formbutton> </div> ) } }

HOC-⾼阶组件

为了提⾼组件复⽤率,可测试性,就要保证组件功能单⼀性;但是若要满⾜复杂需求就要扩展功能单⼀的组件,在React⾥就有了HOC(Higher-Order Components)的概念,定义:⾼阶组件是⼀个⼯⼚函数,它接收⼀个组件并返回另⼀个组件

HOC-基本使⽤

HocPage.js

import React from 'react' function Child(props) { return <div>Child</div> } const foo = (Cmp) => (props) => { return <Cmp {...props} /> } /*const foo = (Cmp) => { return (props) => { return <Cmp {...props} /> } }*/ export default function HocPage(props) { const Foo = foo(Child) return ( <div> HocPage <Foo /> </div> ) }

/pages/User.js【运⽤hoc改写前⾯的Context例⼦】

import React from 'react' import { Consumer } from '../AppContext' import Layout from './Layout' const handleConsumer = (Cmp) => (props) => { return <Consumer>{(ctx) => <Cmp {...props}></Cmp>}</Consumer> } export default function User(props) { const HandleConsumer = handleConsumer(UserCmp) return ( <Layout title="⽤户中⼼"> <HandleConsumer /> </Layout> ) } function UserCmp(props) { console.log('user', props) return <div>User</div> }

HOC-链式调⽤

import React from 'react' function Child(props) { return <div>Child</div> } const foo = (Cmp) => (props) => { return ( <div style={ 
  { background: 'red' }}> <Cmp {...props} /> </div> ) } const foo2 = (Cmp) => (props) => { return ( <div style={ 
  { border: 'solid 1px green' }}> <Cmp {...props} /> </div> ) } export default function HocPage() { const Foo = foo2(foo(Child)) return ( <div> HocPage <Foo /> </div> ) }

HOC-装饰器写法

⾼阶组件本身是对装饰器模式的应⽤,⾃然可以利⽤ES7中出现的装饰器语法来更优雅的书写代码。 CRA项⽬中默认不⽀持js代码使⽤装饰器语法,可修改后缀名为tsx则可以直接⽀持

// 装饰器只能⽤在class上 // 执⾏顺序从下往上 @withLog @withContent class Lesson2 extends React.Component { render() { return ( <div> {this.props.stage} - {this.props.title} </div> ) } } export default function HocTest() { // 这⾥使⽤Lesson2 return ( <div> {[0, 0, 0].map((item, idx) => ( <Lesson2 idx={idx} key={idx} /> ))} </div> ) }

Hooks

Hook是React16.8⼀个新增项,它可以让你在不编写 class 的情况下使⽤ state 以及其他的 React 特性。

Hooks的特点

  • 使你在⽆需修改组件结构的情况下复⽤状态逻辑
  • 可将组件中相互关联的部分拆分成更⼩的函数,复杂组件将变得
  • 更容易理解
  • 更简洁、更易理解的代码

State Hook-状态钩⼦

创建HookPage.js

import React, { useState, useEffect } from 'react' export default function HookPage() { const [date, setDate] = useState(new Date()) useEffect(() => { const timerId = setInterval(() => { setDate(new Date()) }, 1000) return () => clearInterval(timerId) }) return ( <div> <h1>Home ⻚⾯</h1> <div>{date.toLocaleTimeString()}</div> </div> ) }

更新函数类似setState,但它不会整合新旧状态

声明多个状态变量

import React, { useState, useEffect } from 'react' export default function HookPage() { const [date, setDate] = useState(new Date()) const [fruits, setFruits] = useState(['apple', 'banana', 'berry']) useEffect(() => { const timerId = setInterval(() => { setDate(new Date()) }, 1000) return () => clearInterval(timerId) }) const del = (delIndex) => { const tem = [...fruits] tem.splice(delIndex, 1) setFruits(tem) } return ( <div> <h1>Home ⻚⾯</h1> <div>{date.toLocaleTimeString()}</div> <FruitList fruits={fruits} onSetFruits={del} /> </div> ) } function FruitList({ fruits, onSetFruits }) { return ( <> <h2>点击下⾯⽔果删除当前</h2> <ul> {fruits.map((item, index) => { return ( <li key={'fruit' + index} onClick={() => onSetFruits(index)} > {item} </li> ) })} </ul> </> ) }

⽤户输⼊处理

import React, { useState, useEffect } from 'react' export default function HookPage() { const [date, setDate] = useState(new Date()) const [fruits, setFruits] = useState(['apple', 'banana', 'berry']) useEffect(() => { const timerId = setInterval(() => { setDate(new Date()) }, 1000) return () => clearInterval(timerId) }) //副作⽤ , [date]); const del = (delIndex) => { const tem = [...fruits] tem.splice(delIndex, 1) setFruits(tem) } return ( <div> <h1>Home ⻚⾯</h1> <div>{date.toLocaleTimeString()}</div> <FruitAdd onAdd={(item) => setFruits([...fruits, item])} /> <FruitList fruits={fruits} onSetFruits={del} /> </div> ) } function FruitList({ fruits, onSetFruits }) { return ( <> <h2>点击下⾯⽔果删除当前</h2> <ul> {fruits.map((item, index) => { return ( <li key={'fruit' + index} onClick={() => onSetFruits(index)} > {item} </li> ) })} </ul> </> ) } function FruitAdd(props) { const [name, setName] = useState('') return ( <div> <h2>增加⽔果</h2> <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> <button onClick={() => props.onAdd(name)}>add</button> </div> ) }

Effect Hook-副作⽤钩⼦

useEffect:给函数组件增加了执⾏副作⽤操作的能⼒。

副作⽤(Side Effffect):是指⼀个 function 做了和本身运算返回值⽆关的事,⽐如:修改了全局变量、修改了传⼊的参数、甚⾄是console.log(),所以 ajax 操作,修改 dom 都是算作副作⽤。

异步数据获取,更新HooksTest.js

import { useEffect } from 'react' useEffect(() => { setTimeout(() => { setFruits(['⾹蕉', '⻄⽠']) }, 1000) })

测试会发现副作⽤操作会被频繁调⽤

设置依赖

// 设置空数组意为没有依赖,则副作⽤操作仅执⾏⼀次 useEffect(()=>{...}, [])

如果副作⽤操作对某状态有依赖,务必添加依赖选项

useEffect(() => { document.title = fruit }, [fruit])

清除⼯作:有⼀些副作⽤是需要清除的,清除⼯作⾮常重要的,可以防⽌引起内存泄露

useEffect(() => { const timer = setInterval(() => { console.log('msg') }, 1000) return function () { clearInterval(timer) } }, [])

 组件卸载后会执⾏返回的清理函数

Hooks之useReducer

useReducer:useReducer是useState的可选项,常⽤于组件有复杂状态逻辑时,类似于redux中reducer概念。

商品列表状态维护

import React, { useReducer, useEffect } from 'react' import { FruitList, FruitAdd } from './Fruit' function fruitReducer(state, action) { switch (action.type) { case 'init': case 'replace': return action.payload case 'add': return [...state, action.payload] default: return state } } export default function HookReducer() { const [fruits, dispatch] = useReducer(fruitReducer, []) useEffect(() => { setTimeout(() => { dispatch({ type: 'init', payload: ['apple', 'banana'] }) }, 1000) }, []) return ( <div> <h1>User ⻚⾯</h1> <FruitAdd onAdd={(item) => dispatch({ type: 'add', payload: item })} /> <FruitList fruits={fruits} onSetFruits={(cur) => dispatch({ type: 'replace', payload: cur }) } /> </div> ) }

Fruit.js

import React, { useState } from 'react' export function FruitList({ fruits, onSetFruits }) { const delCur = (delIndex) => { const tem = [...fruits] tem.splice(delIndex, 1) onSetFruits(tem) } return ( <> <h2>点击下⾯⽔果删除当前</h2> <ul> {fruits.map((item, index) => { return ( <li key={'fruit' + index} onClick={() => delCur(index)}> {item} </li> ) })} </ul> </> ) } export function FruitAdd(props) { const [name, setName] = useState('') return ( <div> <h2>增加⽔果</h2> <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> <button onClick={() => props.onAdd(name)}>add</button> </div> ) }

Hooks之useContext

useContext⽤于在快速在函数组件中导⼊上下⽂

import React, { useContext } from 'react' const Context = React.createContext() const Provider = Context.Provider export default function HookContext() { const store = { userName: 'xiaoming', } return ( <div> <h1>HookContext ⻚⾯</h1> <Provider value={store}> <Child /> </Provider> </div> ) } function Child(props) { const { userName } = useContext(Context) return ( <div> Child <div>userName: {userName}</div> </div> ) }

Hook相关拓展

  • 基于useReducer的⽅式能否处理异步action
  • Hook规则
  • ⾃定义Hook
  • ⼀堆nb的实现

第三⽅库

// antd-pro安装: yarn create umi // 选择 ant-design-pro npm install npm start

React组件化大纲

  • 课堂⽬标
  • 知识要点
  • 资源
  • 起步
  • 快速开始
  • 组件跨层级通信 - Context
    • 使⽤Context
  • 组件复合-Composition
    • 基本使⽤
  • ⾼阶组件-HOC
    • 基本使⽤
    • 链式调⽤
    • 装饰器写法
  • Hooks
    • 状态钩⼦ State Hook
    • 副作⽤钩⼦ Effect Hook
    • useReducer
    • useContext
    • Hook相关拓展
  • 第三⽅库
  • 下节课内容:第三⽅组件使⽤

 

到此这篇2.React组件化的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

  • 上一篇: 3.React组件化2
  • 下一篇: 1.React核心入门
  • 版权声明


    相关文章:

  • 3.React组件化22024-11-30 22:27:09
  • 4.React全家桶及原理解析2024-11-30 22:27:09
  • react基础笔记2024-11-30 22:27:09
  • 移动端的h5遇到的一些坑记录2024-11-30 22:27:09
  • 移动端左右滑动时,屏蔽上下滑动2024-11-30 22:27:09
  • 1.React核心入门2024-11-30 22:27:09
  • 创建一个新的react项⽬时报错2024-11-30 22:27:09
  • 抓包工具使用&移动端调试2024-11-30 22:27:09
  • 移动端相关信息获取2024-11-30 22:27:09
  • cesium在三维地图中拖拽移动实体位置2024-11-30 22:27:09
  • 全屏图片