react--随笔2

函数式组件

是个函数,不能访问this对象,也就不存在state、实例方法、钩子、也不需要,只能访问props,无需实例化,渲染性能高,适用场景:展示,纯渲染的地方,别名:UI组件,哑组件,无状态组件,木偶组件

const 组件名=(props)=>(jsx)
const 组件名=props=>jsx
const 组件名=(props)=>{
  //业务
  return jsx
}

组件通讯

父子

//单项数据流
<Child 属性=数据/>
this.props.属性

子父

//反向数据流
<Child 属性=父方法/>
this.props.属性(子数据)

中间人

<ChildA 属性=父方法/>
<ChildB 属性=接受的a数据/>

所有 React 组件都必须是纯函数,并禁止修改其自身 props

纯函数:不改变输入参数,输出可控,必须有返回值

refs

<子组件 ref={el=>this.inputRef=el} />

 //父组件
this.inputRef.子属性|state|props|instanceMethod    子父

context组件上下文

Context 旨在共享一个组件树内可被视为 “全局” 的数据,达到越级传递,场景:当前经过身份验证的用户,主题或首选语言,包括管理当前的 locale,theme,或者一些缓存数据

老api

//顶层组件 类属性 组件属性 定义提供上文
static childContextTypes={//类型
  msg: propTypes.string,
  setMsg : propTypes.func
};

getChildContext(){//提供上文
  return {
    msg:this.state.msg,
    setMsg:this.setMsg
  }
}

//下层组件 类属性  接受下文
static contextTypes = {
  msg: propTypes.string,
  setMsg: propTypes.func
};

//使用
this.context.msg | this.context.setMsg(数据)

新api

//Context
import {createContext} from 'react'
const Context = createContext(默认值);//默认值可以不给
export default Context

//祖先组件  Context.Provider包裹组件并且传递属性值
import Context from './Context';
class 祖先组件 extends Component {
  
  state = {
    count: 60
  };

  render() {
    const { count } = this.state;
    return (
      <Context.Provider value={count}>
        ...
        <中间件层组件 />
        ...
      </Context.Provider>
    );
  }
}

//后代组件 Context.Consumer来接收值,Consumer里面不能直接渲染其他组件,而是要声明一个函数。函数的参数就是context的值

import Context from './Context';
export default class Leaf extends Component {
  render() {
    return (
      <Context.Consumer>
        {
          value => {
            return (
              <div className="leaf">
                {value}
              </div>
            )
          }
        }
      </Context.Consumer>
    )
  }
}

//封装Context.Provider

import React,{Component} from "react";

import Context from './Context'
export default class Provider extends Component {
  state={
    count:10
    ...
  };

  increment=(val=1,ev)=>this.setState({count:this.state.count+val})
  decrement=(val=1,ev)=>this.setState({count:this.state.count-val})
	...
  
  render(){
    return (
      <Context.Provider value={
        {
          count: this.state.count,
          increment: this.increment,
          decrement: this.decrement
        }
      }>
        {this.props.children}
      </Context.Provider>
    )
  }
}

//使用封装
<Provider>
	<App/>
</Provider>

订阅发布

pub/sub模式、 消息通知、观察者模式、yarn add pubsub-js -D

  • 订阅: token=pubsub.subscribe(‘消息名’,回调函数(‘消息名’,数据))
  • 发布: pubsub.publish(‘消息名’,数据)
  • 清除指定订阅:pubsub.unsubscribe(token|‘消息名’|回调函数名);
  • 清除所有:pubsub.unsubscribeAll()

路由

let {history,location,match}=props
import {widthRoute}='react-router-dom'

web存储

localStrage、cookie

状态管理

后面学习

高阶组件 HOC

又叫Higher-Order Components,是一个函数能够接受一个组件并返回一个新的组件。组件是将props转化成UI,然而高阶组件将一个组价转化成另外一个组件,例如react-router-dom的widthRouter

就是一个函数接受一个组件作为参数,经过一系列加工后,最后返回一个新的组件,withRouter函数就是一个高阶组件,它返回了一个新的组件,这个组件具有了路由信息。

const withRouter = WrappedComponent => {
  ....  抓取到history,location,match
  return props => <WrappedComponent history={history} {...props} />;
  //return 要求是个类或者函数
};

const Swiper = props => (
  <div class="user-container">
    <p>My name is {props.history}!</p>
  </div>
);
export default withRouter(Swiper);

渲染属性(Render Props)

render prop 是一个用于告知组件需要渲染什么内容的函数,React 组件之间使用一个值为函数的 prop 共享代码的简单技术

class Mouse extends React.Component{

  mouseOver = () => {console.log('over')};
  mouseOut = () => {console.log('out')};

  render(){
    return (
      <div onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>
        {this.props.render()}
      </div>
    )
  }
}

//
<Mouse render={()=>{
    return (
      <>
        <h3>标题</h3>
        <p>段落</p>
        <p>段落</p>
        <p>段落</p>
      </>
    )
  }}/>

状态管理

  • 思想:flux
  • 实现:vuex redux

redux

可以同一个地方查询状态,改变状态,传播状态,用在中大项目,组件状态需要共享,在任何地方都可以拿到,组件需要改变全局状态,一个组件需要改变另外一个组件的状态,创建store实例,其他组件导入并共享这个store实例

redux成员

成员作用类型
createStore创建store实例函数
combineReducers合并多个reducer函数
applyMiddleware安装中间件,改装增强redux函数
compose增强调试开发环境函数

store成员

成员作用类型
subscribe订阅state变化函数
dispatch发送action 给 reducer函数
getState获取一次state的值函数
replaceReducer一般在 Webpack Code-Splitting 按需加载的时候用函数

数据流动

component(views)actionreducerstatecomponent(views)
转发的动作同步业务处理逻辑, 返回copy更新后的state状态收集展示state
store.dispatch—》--------》《–《–subscribe
《–《–getState

操作流程

import {createStore} from 'redux'

//生成默认state 
let defaultState={}

//创建reducer
const reducer = (state=defaultState,action)=>{
  let {type,payload}=action    
  swtich type
    case XXXXX
    更新copy后的state  Object.assign(空,老,新)
  default:
    return state
}

//创建store对象
store = createStore(reducer,state)
export default store;

//组件内部更新,状态获取state
import store from '...'
store.dispatch({type:xxx,payload:ooo}) //发送action给reducer  type是必传参数
store.subscribe(回调)  //订阅 state  更新state时触发
store.getState() //获取状态,执行一次

提取并定义 Action Creators

let nextTodoId = 0;

export const addTodo = text => ({
  type: "ADD_TODO",
  id: nextTodoId++,
  text
});

export const removeTodo = id => ({
  type: "REMOVE_TODO",
  id
});

export const checkNav = bl => ({
  type: "CHECK_NAV",
  bl
});

//处理异步
const updateHome = (collectionName) => dispatch => { //dispatch接受函数 需要thunk中间件
  return axios.get({api:collectionName}).then(
    res=> {
      dispatch({type:'UPDATE_HOME',payload:res.data.data});
      return res//有回执
    }
  )
};

//安装中间件改装 redux  目标:dispatch可以接受一个函数
import {createStore,applyMiddleware,combineReducers} from 'redux'
import thunk from 'redux-thunk'
let store = createStore(rootReducer,rootState,applyMiddleware(thunk));

//组件内部
dispatch(checkNav(!bNav))
dispatch(addTodo('呵呵哒'))

combineReducers提取reducer

当应用逻辑逐渐复杂的时候,我们就要考虑将巨大的 Reducer 函数拆分成一个个独立的单元,这在算法中被称为 ”分而治之“,Reducers 在 Redux 中实际上是用来处理 Store 中存储的 State 中的某个部分,一个 Reducer 和 State 对象树中的某个属性一一对应,一个 Reducer 负责处理 State 中对应的那个属性

// src/plugins/redux
import {createStore,applyMiddleware,combineReducers} from 'redux'
import thunk from 'redux-thunk'
import todos from '../store/reducers/todos'
import bNav from '../store/reducers/nav'
let rootReducer=combineReducers({bNav,todos});
let store = createStore(rootReducer,applyMiddleware(thunk));
export default store;

// src/store/reducers/todos
let initState=[]

const todos = (todos=initState, action) => {
  switch (action.type) {
    case "ADD_TODO": {
      return [
        ...todos,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    }

    case "REMOVE_TODO": {
      const { id } = action;
      todos.map((item,index) => item.id ===id && todos.splice(index, 1));
      return [...todos]
    }

    case "CHECK_TODO": {
      const { id } = action;
      todos.map((item,index) => item.id ===id && (todos[index].completed=!todos[index].completed));
      return [...todos]
    }

    default:
      return todos;
  }
};

export default todos;

// src/store/reducers/bNav
const bNav = (bNav=false, action) => {
  switch (action.type) {
    case "CHECK_NAV": {
      const { bl } = action;
      return bl
    }

    default:
      return bNav;
  }
};

export default bNav;

state数据不写在构造器内订阅,可以写在主入口文件 订阅react-dom的更新

let render = ()=>{
    ReactDOM.render(
      <App/>,
      document.getElementById('root')
    )
};
render();
store.subscribe(render);

react-redux

基于redux思想,专门为react使用redux而生,把组件拆分为容器组件, UI组件,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它

UI组件
  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API
容器组件
  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API
最佳实现
//主入口
import {Provider} from react-redux
import store from './plugins/redux'
<Provider store={redux打造的store}>
  <根组件/>
</Provider>
  
  
        
//Creators改装  把异步actins内部有关,api请求的通用部分封装出来的一个过程

//api
const get = ({api,_page=1,_limit=10,id=null}) => (
  axios({
    url: id ? `/mock/${api}/${id}` : `/mock/${api}`,
    params: {_page,_limit}
  })
);

//actionsCreators
const clearHome={type: 'CLEAR_HOME'};//dispatch接受对象 默认

const updateHome = () => dispatch => { //dispatch接受函数 需要thunk中间件
  return get({api:'home'}).then(
    res=> {
      dispatch({type:'UPDATE_HOME',payload:res.data.data});
      return res//有回执
    }
  )
};

const updateBANNER=()=>async dispatch => {
  let res = await get({api:'banner'});
  dispatch({type:'UPDATE_BANNER',payload:res.data.data})
};

export {clearHome,updateHome,updateBANNER}


//UI组件 
const Home = ({home, banner,dispatch}) => {
  useEffect(() => {
    dispatch(clearHome);
    dispatch(updateHome()).then(data => 收取回执)
    dispatch(updateBANNER())
  }, []);
  
  return (
    <div className="Home">

      <Swiper data={banner}/>
      {
        home.map(item => (
          <Cell key={item.id} item={item} dataName="home"/>
        ))
      }

    </div>
  )
};

//容器组件 dispatch方法 默认传递给UI组件
export default connect(
  state=>({banner:state.banner, home:state.home})
)(Home)

redux-devtools使用

import {createStore,combineReducers,applyMiddleware,compose} from 'redux';
//compose 增强器

import thunk from 'redux-thunk'

let rootReducer = combineReducers({banner, column, detail, follow, home, user});

//使用redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
let store = createStore(rootReducer,composeEnhancers(applyMiddleware(thunk)));//安装了中间件,改装了redux

export default store;

token加入redux 做持久化处理

思路1:登录、注销、强刷同步redux,axios拦截器只读redux,为了速度,跳转有axios完成, 问题是组件会渲染,再去跳转

思路2:redux里面准备一条数据,axios和其他组件都去修改他 , 或使用它,无需跳转,顶层准备一个BaseLayout布局组件,返回的界面根据这条数据响应式渲染

//BaseLayout.jsx
export const connect(user=> state.user)(function BaseLayout({user:{err}, ...rest}){
    if (err==1) { return <Login {...rest} /> }
    if (err==3) { return <Reg /> }
    return <default {...rest}/>
})

思想3: 前置路由守卫(部分路由独享),负责跳转

片段

为一个组件返回多个根级元素。 可以让你将多个子元素列表添加到一个分组中,可以接受业务逻辑,并且不会在DOM中增加额外节点

<React.Fragment key="bmw"></..>
<></>

//只接受 key` and `children 属性

异步组件

把静态导入的组件,变成一个可以返回promise的函数,函数内部在路由跳转时,去异步加载目标组件,关键字import(),create-react-app 环境 webpack自动分片打包

//import 语法
import ("./ChildB.js").then(
  ChildB=>console.log(ChildB.Default) // new ChildB.Default() 实例化
)

//方式1
const Follow = asyncComponent(()=>import("./Follow"))

export default function asyncComponent(importComponent) {
  class AsyncComponent extends Component {
    constructor(props) {
      super(props);

      this.state = {
        component: null
      };
    }

    async componentDidMount() {
      const { default: component } = await importComponent();

      this.setState({
        component: component
      });
    }

    render() {
      const C = this.state.component;
      return C ? <C {...this.props} /> : null;
    }
  }

  return AsyncComponent;
}

//方式2
import Loadable from 'react-loadable';
const Loading = () => <div>Loading...</div>;
const Home = Loadable({
  loader: () => import('./routes/Home'),
  loading: Loading,
  loading:()=>{return null}
});

PureComponent

  • 使用PureCompoent是因为它是一个更具性能的Component的版本
  • 性能的提高还伴随着一些附加的条件
  • 提供了具有浅比较的shouldComponentUpdate方法
  • 当props或者state改变时,PureComponent将对props和state进行浅比较
  • Component的shouldComponentUpdate构造被调用默认重渲,PureCompoent不一定
  • 不能再重写shouldComponentUpdate
  • 不渲染的情况: 父组件中改变对象的子键,子组件比较的是引用是否相同,
  • 不要在render方法中创建一个新的函数,对象或者是数组
  • 场景:组件收到的props和定义的state是基本类型时,复合类型更新时修改根键时

UI库

Ant Design

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

特性

  • 🌈 提炼自企业级中后台产品的交互语言和视觉风格。
  • 📦 开箱即用的高质量 React 组件。
  • 🛡 使用 TypeScript 开发,提供完整的类型定义文件。
  • ⚙️ 全链路开发和设计工具体系。
  • 🌍 数十个国际化语言支持。
  • 🎨 深入每个细节的主题定制能力。

安装

yarn add antd --save

按需引入

yarn add babel-plugin-import --save

webpack loader配置,找到babel-loader  按需引入配置   394行
+ options 项目
"plugins": [
  ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }], 
  // `style: true` 会加载 less 文件 pc

]

使用组件

import {LocaleProvider, DatePicker,Button } from 'antd';

修改文案

// 方案1  V3
import zhCN from 'antd/lib/locale-provider/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

//组件需要被 包裹
<LocaleProvider locale={zhCN}>
  <App/>
</LocaleProvider>

//方案2  V4
import zhCN from 'antd/es/locale/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

return (
  <ConfigProvider locale={zhCN}>
    <App />
  </ConfigProvider>
);

栗子***

antd-mobile

安装

yarn add antd-mobile --save

按需引入

yarn add babel-plugin-import --save 

//webpack loader配置,找到babel-loader  按需引入配置
// + options 项目
"plugins": [
    ["import", { libraryName: "antd-mobile", style: "css" }] 
  // `style: true` 会加载 less 文件 touch  pc端配置和touch端配置不可并存
]

使用组件

import { DatePickerView } from 'antd-mobile'; //直接使用组件 文案是中文
//import 'antd-mobile/lib/DatePickerView/style/css';  手动 

栗子

//Tabbar组件
//TabBar>TabBar.Item + 数据(title,key,path,icon,selectedIcon)

//路由: 
history.push(this.state.tabs[index].path)

//监听: 
static getDerivedStateFromProps(nextProps,nextState)  {} 
location.pathname.indexOf(item.path)
setState->selectedTab:item.key

//home
Flex 组件
  Flex>Flex.Item  style={{flex:0.6}} 约定比例
  WhiteSpace 上下留白
Carousel 走马灯
  Link>img
Grid 宫格
Tabs 标签页

//category
分段器手写 + Route

//follow/column
PullToRefresh 拉动刷新
				List 列表
					List.Item  history.push(编程式跳转)
						List.Item.Brief
//detail
NavBar导航 
    箭头样式 写入base.css 覆盖默认,同类共用
    WingBlank 两侧留白
    Flex>Flex.Item

//shopcart
SwipeAction 滑动操作
	List>SwipeAction>List.Item>Stepper步进器

//user
卡片 card
  Card>Card.Header|Body>Badge 徽标
  Card>Card.Header|Body>Grid 宫格
NoticeBar 通告栏

//登录|注册
InputItem 文本输入
Button 按钮
行间样式修改 覆盖样式

mobx

一款可以与redux媲美的数据流方案,Flux思想单向数据流方案,以 Redux 为代表,Reactive响应式数据流方案,以 Mobx 为代表

  • 单向数据流实现:redux + react-redux + react-thunk + redux-saga
  • 响应式数据流实现:mobx + mobx-react

MobX 的理念是通过观察者模式对数据做出追踪处理,在对可观察属性作出变更或者引用的时候,触发其依赖的监听函数,整体的store注入机制采用react提供的context来进行传递

适用场景可以是react vue angular mpvue 小程序 taro

装饰器Decorator

是个函数,用来装饰类或者类成员 ,是Object.defineProperty的语法糖

//给对象添加或修改属性
Object.defineProperty(target, prop, desc)
//target 需要定义属性的当前对象
//prop 当前需要定义的属性名 类型:字符
desc默认值说明
configurablefalse描述属性是否可以被删除,默认为 false
enumerablefalse描述属性是否可以被for…in或Object.keys枚举,默认为 false
writablefalse描述属性是否可以修改,默认为 false
getundefined当访问属性时触发该方法,默认为undefined
setundefined当属性被修改时触发该方法,默认为undefined
valueundefined属性值,默认为undefined
//定义装饰器
function 装饰器名 (target,prop,descriptor){
  descriptor.writable=false;//writable属性是否可以写入
  return descriptor;
}

//使用
@装饰器名 类
@装饰器名 类的实例属性|静态属性
@装饰器名 类的实例方法|静态方法
@装饰器名(参数) 类的实例属性|静态属性
装饰器名(类的实例属性|静态属性)

//使用场景
mobx / angluarTs / vueTs / reactTs / java ...

配置

cra脚手架 不支持装饰器语法,需要小配一下

yarn add @babel/plugin-proposal-decorators --save

package.json

babel: {
  "presets":...

  +
  "plugins": [
      ["@babel/plugin-proposal-decorators", { "legacy": true }],
   ]

  ....
}

vscode编辑配置

vscode->设置->搜索设置输入:experimentalDecorators->勾上
//webstrom 无需设置

mobx成员

//mobx 6.0 -   
import {observable,action} from 'mobx'
@observable 装饰store类的成员,为被观察者
@action 实例方法, 处理实例属性,修改状态,不推荐组件内部改

//mobx 6.0 + 变更 无需装饰器成员
import {makeAutoObservable} from 'mobx'
constructor(){
    makeAutoObservable(this);
}

mobx-react成员

inject observer Provider

Provider,顶层提供store的服务

<Provider store={store} key={store.key}></Provider>

inject,注入Provider提供的store到该组件的props中,组件内部使用,inject 是一个高阶组件 高阶组件返回的是组件,作用在包装组件

//注入所有内容 
export default inject('store')(组件)

//注入指定内容
export default inject(
  stores => ({key:stores.xx.oo})
)(组件)

@inject 是装饰器,装饰的是类本身和类成员

@inject('store') class 类组件

observer,设置当前组件为观察者,一旦检测到store中被监测者发生变化就会进行视图的强制刷新

@observer class 类组件
  
const 函数式组件=observer((store)=>{jsx})

构建

程序主入口

import {Provider} from 'mobx-react'
import store from './store';
<Provider store={store}>所有</.>

store

// src/store/index
import User from './user'
...

class Store {

  constructor(){
    this.user = new User(this);//传递this防止this丢失
   	.... 可以把组件的数据交给一个一个类来处理,有的模块管理的感觉
  }

}
export default new Store();


// src/store/user
import { observable, action } from 'mobx'
import axios from "axios";

class User {
	
  //被观测者
  @observable user= window.localStorage.getItem('1909_newsapp') ?
    JSON.parse(window.localStorage.getItem('1909_newsapp')) :
    {
      err:1,
      msg:'未登录',
      data:{}
    };

  constructor(store){
    this.store=store;
  }
	
	//处理被观测者数据
  @action check = async ({api,method='get',username,password}) => {
    return axios({
      url:`/api/${api}`,
      method,
      params: method === 'get' ? {username, password}: null,
      data: method === 'post' ? {username, password}: null,
    }).then(
      res=>{
        this.user = res.data;
        window.localStorage.setItem('xxx',JSON.stringify(res.data));
        return res
      }
    )
  };
}

export default User;

//组件注入 被做一个观察者
import {inject, observer} from "mobx-react";

@inject('store')
@observer
export default class Home extends React.Component{
  constructor(props){
    super(props);
    props.store.goods.update({
      ...
    })
  }

  render(){
    let {goods:{home,banner}}=this.props.store;
    return(
      ...
    )
  }
}
  
//面对函数式组件
const react函数式组件=observer((store)=>{jsx})
export default inject('store')(react函数式组件)

hooks 钩子

Hook 使你在非 class 的情况下可以使用更多的 React 特性,React为什么要搞一个Hooks,想要复用一个有状态的组件太麻烦了!我们都知道react都核心思想就是,将一个页面拆成一堆独立的,可复用的组件,并且用自上而下的单向数据流的形式将这些组件串联起来。但假如你在大型的工作项目中用react,你会发现你的项目中实际上很多react组件冗长且难以复用。尤其是那些写成class的组件,它们本身包含了状态(state),所以复用这类组件就变得很麻烦,那之前,官方推荐怎么解决这个问题呢?答案是:渲染属性(Render Props)和高阶组件(Higher-Order Components),hooks为共享状态逻辑提供更好的原生途径,使你在无需修改组件结构的情况下复用状态逻辑

版本支持上,16.7.0-alpha 开始支持 16.8.0 第一个正式版

使用规则

  • Hook可让您在不编写类的情况下使用状态和其他React功能
  • 只能在顶层调用Hooks 。不要在循环,条件或嵌套函数中调用Hook
  • 只能在functional component或者自定义钩子中使用Hooks
  • 钩子在类内部不起作用,没有计划从React中删除类

useState 状态

import { useState } from 'react';
const [状态变量, 状态方法] = useState(状态属性的初始值);
const [count, setCount] = useState(0);

//使用状态
{状态变量}  //返回 状态值

//修改状态
setCount(新值)

可以自由命名,状态变量可以不只一个state变量了

useEffect 生命周期

每当 React更新之后,就会触发 useEffect,在第一次 render 和每次 update 后触发,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

import { useEffect } from 'react'; 

useEffect(()=>{
  //didMount || didUpdate
  return ()=>{willUnmount}
},[])

[] == didMount ,不传递==didMount + didUpdate

[state|props] == 指定的state或者props变化时

每一个state|props可以拥有一个effect(关注点分离),按照 effect 声明的顺序依次调用

return 函数,在需要清除副作用时使用

第一个参数(函数),不允许async修饰,不允许返回任何值

useRef 元素引用

返回一个可变的ref对象,current属性初始化为传递的参数initialValue

let refContainer = useRef(initialValue)   // ~~ React.createRef(init)

<JSX ref={refContainer} ...

refContainer.current  -> dom操作

自定义钩子 useXxxXxx

  • 重用不同组件之间的常见有状态业务逻辑。
  • 但每次使用自定义钩子时,其中的所有状态和效果都是完全隔离的
  • 我必须以“ use” 开头命名我的自定义Hook
  • 自定义Hook是一个JavaScript函数,其名称以“ use” 开头,可以调用其他Hook
function useList(initList) {

  //使用系统和自定义钩子
  let [list, setList] = useState(initList);

  //业务
  function add(item) {
    alert('add')
    setList([...list, item])
  }

  function del(index) {
    let arr = [...list];
    arr.splice(index, 1);
    setList(arr);
  }

  function check(index, key,value) {
    alert('check')
    let arr = [...list];
    arr[index][key] = value;
    setList(arr);
  }

  // return [list, add, del, check]
  return {list, add, del, check}
}

//上面的业务,可以被购物结算和留言列表多个组件复用

useContext

不使用组件嵌套就可以订阅 React 的 Context,useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 MyContext.Consumer

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Xxx />
    </ThemeContext.Provider>
  );
}

function Xxx(props) {
  return (
    <div>
      <Ooo />
    </div>
  );
}

function Ooo() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      ...
    </button>
  );
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值