课堂目标
- 掌握组件化开发中多种实现技术
- 了解组件化概念,能设计并实现⾃⼰需要的组件
- 掌握使⽤跨层级通信-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组件化的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/yd-react-native/11104.html