React项目知识积累(五)

1.dispatch、dev派发

src/models/formStatus.js:

const FromStatusModel = {
    namespace: "fromStatus",
    state: {
        isDisable: false,
    },
    reducers: {
        saveIsDisable(state, { payload }) {
            return {
                ...state,
                ...payload,
            };
        },
    },
};

export default FromStatusModel;

改变和提交的js文件写法:

import { useDispatch, useSelector } from 'umi';

const dispatch = useDispatch();
const { isDisable } = useSelector((state) => state.formStatus);

const onclick = () => {
  dispatch({
    type: 'formStatus/saveIsDisabled',
    payload: { isDisable: isDisable === true ? false : true },
  });
};

<Button type="primary" onclick={onclick()}>
  dev派发
</Button>
<div>是否可以传递{isDisable}</div>

通过useSelector进行传值,通过dispatch进行改变内容。

在 React 和 Redux 的上下文中,"dispatch" 是一个核心概念,它指的是派发(发送)一个 action,这是一个告诉 Redux store 如何更新状态的普通 JavaScript 对象。在 Redux 中,dispatch 是由 Redux 提供的一个 action 派发器(action dispatcher)。

当你在 Redux 应用中派发一个 action 时,Redux store 会根据这个 action 中的信息来更新其状态。这个过程通常是通过 dispatch 一个由 actionCreator 返回的 action 来完成的。

// 假设有一个 actionCreator 函数
function addTodo(text) {
  return {
    type: 'ADD_TODO',
    payload: text
  };
}

// 在一个组件中,你可能会这样使用 dispatch
const dispatch = useDispatch();

function handleClick() {
  dispatch(addTodo('Learn UMI'));
}

在这个例子中,addTodo 是一个 actionCreator,它返回一个 action 对象。当 handleClick 函数被调用时,它 dispatch 这个 action,导致 Redux store 更新。

在开发过程中,你可能会使用如下的做法来派发 action:

// 使用 Redux 的 actionCreator 创建一个 action
const action = addTodo('Learn UMI');

// 派发 action
dispatch(action);

如果你是在寻找如何在 React 应用中使用 Redux 的 dispatch 方法,那么关键是要确保你有一个 Redux store(通过 Provider 组件提供给你的应用),并且你的组件是通过 useDispatch 钩子来获取 dispatch 函数的。

这个dev中可以加函数操作:

effects: {
  *fetchNotices(_, { call, put, select }) {
    // 调用queryNotices函数来获取通知数据
    const data = yield call(queryNotices);
    // 把获取到的数据放入redux的global reducer的notices字段
    yield put({
      type: "saveNotices",
      payload: data,
    });
    // 计算未读通知的数量
    const unreadCount = yield select((state) => state.global.notices.filter((item) => !item.read).length);
    // 更新用户通知的总数和未读数
    yield put({
      type: "user/changeNotifyCount",
      payload: {
        totalCount: data.length,
        unreadCount,
      }
    })
  },
  *clearNotices({ payload }, { put, select }) {
    // 清除特定的通知
    yield put({
      type: "saveClearedNotices",
      payload,
    });
    // 获取通知的总数
    const count = yield select((state) => state.global.notices.length);
    // 获取未读通知的数量
    const unreadCount = yield select((state) => state.global.notices.filter((item) => !item.read).length);
    // 更新用户通知的总数和未读数
    yield put({
      type: "user/changeNotifyCount",
      payload: {
        totalCount: count,
        unreadCount,
      }
    });
  },
  *changeNoticeReadState({ payload }, { put, select }) {
    // 改变特定通知的阅读状态
    const notices = yield select((state) => state.global.notices.map((item) => {
      const notice = { ...item };
      if (notice.id === payload) {
        notice.read = true;
      }
      return notice;
    }));
    // 更新redux的global reducer的notices字段
    yield put({
      type: "saveNotices",
      payload: notices,
    });
    // 计算新的未读通知数量
    const unreadCount = yield select((state) => state.global.notices.filter((item) => !item.read).length);
    // 更新用户通知的总数和未读数
    yield put({
      type: "user/changeNotifyCount",
      payload: {
        totalCount: notices.length,
        unreadCount,
      }
    })
  }
}

使用Redux Saga中间件处理异步逻辑的例子。它定义了三个sagaEffects,用于处理通知(notices)的不同操作:

  1. fetchNotices: 获取通知数据,并更新状态和未读通知计数。
  2. clearNotices: 清除通知,并更新状态和通知计数。
  3. changeNoticeReadState: 改变特定通知的阅读状态,并更新状态和未读通知计数。

请注意,这段代码假设您已经设置了一个saga中间件,并且您的action类型(如saveNoticesuser/changeNotifyCount)和reducer已经正确地在您的应用程序中定义。此外,queryNotices应该是一个返回Promise的外部函数,它负责从服务器获取通知数据。

  1. *fetchNotices(_, { call, put, select }) { ... }:这是一个使用了 Generator 函数的 Saga,它定义了一个名为 fetchNotices 的异步操作。在这个 Saga 中,它首先调用了 queryNotices 函数来获取通知数据,然后将数据保存到应用的状态中,并更新用户的通知数量信息。

  2. *clearNotices({ payload }, { put, select }) { ... }:这是另一个使用了 Generator 函数的 Saga,它定义了一个名为 clearNotices 的异步操作。在这个 Saga 中,它首先保存清除后的通知数据到应用状态中,然后更新用户的通知数量信息。

  3. *changeNoticeReadState({ payload }, { put, select }) { ... }:这也是一个使用了 Generator 函数的 Saga,它定义了一个名为 changeNoticeReadState 的异步操作。在这个 Saga 中,它首先更新通知的阅读状态,然后保存更新后的通知数据到应用状态中,并更新用户的通知数量信息。

总之,这段代码展示了如何使用 Redux-Saga 来处理异步操作和副作用。通过使用 Generator 函数和 effects,可以很好地管理应用中的异步逻辑,并且触发 action 来更新应用的状态。这种方式可以有效地处理复杂的异步操作流程,并且使代码更加清晰和易于维护。

2.页面组件逻辑过多时的写法!

const newDatas = {
  dataOne: (obj) => <div style={{ color: 'black' }}>{obj}</div>,
  dataTwo: (obj) => <div style={{ color: 'red' }}>{obj}</div>,
  dataThree: (obj) => <div style={{ color: 'blue' }}>{obj}</div>,
};

<>
  {newDatas.dataOne('第一个')}
  {newDatas.dataTwo('第二个')}
  {newDatas.dataThree('第三个')}
</>

可以将不同逻辑的组件分开写,虽然写法不好看,但是非常的规则,非常的整齐!

3.ProFormDependency 多级联动

import { ProFormRadio, ProForm, ProFormDependency } from '@ant-design/pro-form';

      <ProForm onFinish={() => {}}>
        <ProFormRadio.Group
          name="client_type"
          label="类型选择"
          initialValue="一般"
          options={[
            { label: '一般', value: '一般' },
            { label: '二般', value: '二般' },
            { label: '三般', value: '三般' },
          ]}
          fieldProps={{
            optionType: 'button',
            buttonStyle: 'solid',
            onChange: (e) => {
              if (e?.target?.value === '一般') {
                console.log('一般');
              } else {
                console.log('其他');
              }
            },
          }}
          rules={[{ required: true, message: '必填' }]}
        />
        <ProFormDependency name={['client_type']}>
          {({ client_type }) => {
            return ['一般', '四般', '五般']?.includes(client_type) && <div>显示下一级</div>;
          }}
        </ProFormDependency>
      </ProForm>
  • npm i @ant-design/pro-components --save

调用组建的时候需要安装依赖,单独每个模块进行安装依赖!

4.JavaScript 有哪些数据类型,它们的区别?

JavaScript 共有八种数据类型,分别是 Undefined、Null、Boolean、 Number、String、Object、Symbol、BigInt。

其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:

●Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了 解决可能出现的全局变量冲突的问题。

●BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数, 使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。

这些数据可以分为原始数据类型和引用数据类型:

●栈:原始数据类型(Undefined、Null、Boolean、Number、String)

●堆:引用数据类型(对象、数组和函数) 两种类型的区别在于存储位置的不同:

●原始数据类型直接存储在栈(stack)中的简单数据段,占据空间 小、大小固定,属于被频繁使用数据,所以放入栈中存储;

●引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固 定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈 中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引 用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。 堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:

●在数据结构中,栈中数据的存取方式为先进后出。

●堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大 小来规定。 在操作系统中,内存被分为栈区和堆区: 

●栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的 值等。其操作方式类似于数据结构中的栈。

●堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可 能由垃圾回收机制回收。

5.数据类型检测的方式有哪些

(1) typeof 

typeof "sty"   //string   

数据类型:number、boolean、string、object、function、undefined

其中:数组,对象,null都会被判断为object。

(2) instanceof

"str" instanceof String //

instanceof 可以正确判断对象的类型,其内部运行机制是判断在其 原型链中能否找到该类型的原型。

instanceof 只能正确判断引用数据类型,而不能判断基 本数据类型。instanceof 运算符可以用来测试一个对象在其原型链 中是否存在一个构造函数的 prototype 属性。

// 例子 1
const obj = new Object();
console.log(obj instanceof Object); // true,因为 obj 是 Object 的实例

// 例子 2
class MyClass {}
const myInstance = new MyClass();
console.log(myInstance instanceof MyClass); // true,因为 myInstance 是 MyClass 的实例

// 例子 3
const arr = [1, 2, 3];
console.log(arr instanceof Array); // true,因为 arr 是 Array 的实例

// 例子 4
console.log(null instanceof Object); // false,因为 null 是 null,不是 Object 的实例

(3) constructor

在 JavaScript 中,constructor 属性是一个对象的内部属性,它指向对象的构造函数。每个对象都有一个 constructor 属性,该属性指向创建该对象的函数。

以下是 constructor 属性的几个关键点:

  1. 访问:你可以通过对象实例直接访问 constructor 属性。例如:

    const obj = {};
    console.log(obj.constructor === Object); // true,因为 obj 是 Object 的实例
    
  2. 默认行为:在大多数情况下,当你创建一个对象时,它的 constructor 属性会自动设置为创建该对象的函数。例如:

    const obj = new Object();
    console.log(obj.constructor === Object); // true
    
  3. 重写:你可以通过手动设置 constructor 属性来改变它的值。例如:

    const obj = {};
    obj.constructor = Array;
    console.log(obj.constructor === Array); // true
    
  4. 原型链:如果你查看一个对象的原型,你会发现 constructor 属性指向了原型链上的构造函数。例如:

    const arr = [];
    console.log(arr.constructor === Array); // true
    
  5. 自定义构造函数:当你定义一个自定义的构造函数时,constructor 属性会指向该构造函数。例如:

    class MyClass {}
    const myInstance = new MyClass();
    console.log(myInstance.constructor === MyClass); // true
    
  6. 使用 constructor 属性:虽然 constructor 属性是一个有用的属性,但并不是所有对象都使用它来初始化实例。例如,在箭头函数中,constructor 属性被设置为 Function 而不是 ArrowFunction。因此,在使用 constructor 属性来判断对象的类型时,需要小心。

总之,constructor 属性是一个方便的方式来访问对象的构造函数,但它并不总是准确地反映对象的实际类型。在判断对象类型时,通常建议使用 instanceof 运算符或 Object.prototype.toString.call() 方法来获取更准确的信息。

(4) Object.prototype.toString.call()

Object.prototype.toString.call() 是一种在 JavaScript 中检查对象类型的方法,它可以返回一个表示对象类型的字符串。这个方法使用 Object.prototype.toStringcall 方法(或 apply 方法),通过传递一个对象作为参数来调用它。

Object.prototype.toString 方法的默认行为会返回一个表示对象类型的字符串,格式通常为 [object ClassName]。例如,对于一个数组,它会返回 [object Array];对于一个函数,它会返回 [object Function]

以下是如何使用 Object.prototype.toString.call() 来检查对象类型的示例:

console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(function() {})); // [object Function]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(new Error())); // [object Error]
console.log(Object.prototype.toString.call('hello')); // [object String]
console.log(Object.prototype.toString.call(123)); // [object Number]

使用 Object.prototype.toString.call() 方法的好处是它非常准确,因为它利用了对象的内部属性来判断类型。然而,这个方法也有一些缺点:

  1. 性能:这个方法比简单的 instanceof 运算符或 constructor 属性要慢,因为它涉及到调用一个方法。

  2. 不可预测的结果:如果你手动改变了对象的 constructor 属性,那么使用 Object.prototype.toString.call() 可能会得到不可预测的结果。

  3. 兼容性:虽然这个方法在所有现代浏览器中都是可用的,但在旧版本的浏览器中可能不支持。

总的来说,Object.prototype.toString.call() 是一个强大的工具,可以用于检查 JavaScript 对象的类型。但与任何工具一样,应根据具体需求和上下文来决定是否使用它。

6.null 和 undefined 区别

在 JavaScript 中,nullundefined 是两个不同的概念,它们表示不同的“无值”状态。

  1. null

    • null 是一个表示“无值”的原始值,即它表示一个明确地没有对象值的情况。
    • 当一个变量被显式地赋值为 null 时,它的值就是 null
    • 在数值上下文中,null 被视为 0 或 false,具体取决于上下文。
    • typeof null 的结果是 "object",这是一个历史遗留问题,因为最初 null 被设计为对象类型。
  2. undefined

    • undefined 是一个表示“未定义”的原始值,即它表示一个变量已声明但尚未被赋值的情况。
    • 当一个变量被声明了但没有被赋值时,它的值就是 undefined
    • 在数值上下文中,undefined 被视为 NaN(Not a Number)。
    • typeof undefined 的结果是 "undefined"

区别:

  • 用途不同:null 通常用于显式地设置一个变量为“无值”,而 undefined 通常用于表示一个变量尚未被赋值。
  • 数值处理不同:在数值上下文中,null 被视为 0 或 false,而 undefined 被视为 NaN。
  • 类型标记不同:typeof null 返回 "object",而 typeof undefined 返回 "undefined"

示例:

let a; // a 此时是 undefined
a = null; // a 现在是 null
console.log(a); // null
console.log(typeof a); // object,这是因为在 JavaScript 中 null 被视为对象类型
console.log(a + 1); // 0,因为 null 在数值上下文中被视为 0
console.log(a === null); // true
console.log(a == null); // true,这表示 a 是否为 null 或 undefined
console.log(a === undefined); // false
console.log(typeof b); // undefined,因为 b 从未被声明

总结:nullundefined 都是用来表示“无值”的状态,但它们在用法和含义上有所不同。在处理 JavaScript 中的“无值”状态时,了解这两个概念的区别是很重要的。

当对这两种类型使用 typeof 进行判断时,Null 类型化会返回 “object”,这是一个历史遗留的问题。当使用双等号对两种类型的 值进行比较时会返回 true,使用三个等号时会返回 false。

7.intanceof 操作符的实现原理及实现

instanceof 操作符是 JavaScript 中用于检查一个对象是否为某个构造函数的实例。其基本用法如下:

object instanceof constructor

其中,object 是需要检查的对象,constructor 是一个构造函数。如果 objectconstructor 的实例,或者 object 的原型链中包含 constructor.prototype,那么表达式的结果为 true;否则为 false

实现原理

instanceof 操作符的实现原理涉及原型链的搜索。具体步骤如下:

  1. 获取 object 的 [[Prototype]](即 object 的原型)。
  2. 检查 [[Prototype]] 是否等于 constructor.prototype
  3. 如果等于,则返回 true
  4. 否则,继续查找 [[Prototype]] 的 [[Prototype]],即 [[Prototype]] 的原型。
  5. 重复步骤 2 和 3,直到找到 constructor.prototype 或者 object 的原型链结束(即 object 的原型链中不再包含 constructor.prototype 的原型)。
  6. 如果整个原型链搜索结束,仍然没有找到 constructor.prototype,则返回 false

实现

下面是一个简单的 instanceof 操作符的实现示例:

function myInstanceof(object, constructor) {
  let proto = Object.getPrototypeOf(object);
  let constructorProto = constructor.prototype;

  while (proto !== null) {
    if (proto === constructorProto) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }

  return false;
}

// 使用示例
console.log(myInstanceof({}, Object)); // true
console.log(myInstanceof([], Array)); // true
console.log(myInstanceof(null, Object)); // false
console.log(myInstanceof(123, Number)); // false

这个实现是基于原型链的搜索原理,通过不断查找 object 的原型,直到找到 constructor.prototype 或者原型链结束。

需要注意的是,这个实现没有考虑 instanceof 操作符的细节,比如对 Symbol 类型和 BigInt 类型的支持,以及 constructor 是否应该是一个实际的构造函数。在实际环境中,instanceof 操作符的实现会更加复杂和全面。

  1. __proto__ 属性:虽然大多数现代浏览器都支持 __proto__ 属性,但这个属性并不是所有 JavaScript 环境都支持的(例如,Node.js)。因此,使用 __proto__ 可能会导致跨环境的兼容性问题。在标准 JavaScript 中,应该使用 Object.getPrototypeOf 方法来获取对象的原型。

  2. null 检查:在原型链的搜索中,如果遇到 null,应该立即返回 false。这是因为 null 没有原型,不应该在原型链中继续搜索。

  3. 循环退出条件:如果原型链中没有 constructorProto,函数应该返回 false,而不是在无限循环中。

  4. 性能:在每次调用 instanceof 时,这个函数都会执行一个循环,直到找到匹配或者原型链结束。对于大型对象或深层原型链,这可能会导致性能问题。

8. 如何获取安全的 undefined 值?

在 JavaScript 中,undefined 是一个表示“未定义”的原始值,它代表了变量的值尚未被赋值时的状态。由于 undefined 是一个全局变量,直接赋值给一个变量会导致 undefined 被污染,从而影响 undefined 的正常判断。

为了获取一个安全的 undefined 值,可以使用 void 0 表达式。这是因为 void 操作符用于执行一个表达式,但不返回任何值。当 void 0 被执行时,它不返回任何值,但实际上执行的是 undefined 赋值操作,因此它等同于 undefined

这里有一个例子:

let safeUndefined = void 0;
console.log(safeUndefined); // undefined

在这个例子中,safeUndefined 被赋值为 void 0,即 undefined。这样,我们就得到了一个安全的 undefined 值,它不会污染全局的 undefined 变量。

使用 void 0 来获取 undefined 是一种在函数中传递参数时保持参数原始值的方法,特别是在使用模块化编程时,这种方法可以帮助保持代码的清晰性和可维护性。

9.Object.is() 与比较操作符 “===”、“==” 的区别?

Object.is() 是 ECMAScript 6 (ES6) 引入的一个新的比较操作符,它用于比较两个值是否完全相等。Object.is() 与传统的比较操作符 ===== 有以下区别:

  1. Object.is(value1, value2)

    • 比较两个值是否严格相等,即值和数据类型都必须相同。
    • 对于 NaNObject.is(NaN, NaN) 返回 true,而 === 和 == 都会返回 false
    • 对于 +0 和 -0Object.is(+0, -0) 返回 false,而 === 和 == 都会返回 true
  2. ===(严格等于)

    • 比较两个值是否严格相等,即值必须相同,但数据类型可以不同。
    • 对于 NaN,=== 返回 false,因为它与任何值都不相等,包括它自己。
    • 对于 +0 和 -0,=== 返回 true,因为在数学上它们是相等的。
  3. ==(等于)

    • 比较两个值是否相等,如果数据类型不同,会进行类型转换,然后再进行比较。
    • 对于 NaN,== 返回 false,因为它与任何值都不相等,包括它自己。
    • 对于 +0 和 -0,== 返回 true,因为它们在数值上下文中被视为相等。

总结:

  • Object.is() 提供了与 === 类似的行为,但处理了 NaN 和 +0/-0 的特殊情况。
  • === 用于需要严格相等比较的情况,不进行类型转换。
  • == 用于需要进行类型转换的比较,可能返回意外的结果。

使用 Object.is() 可以帮助你更准确地比较两个值,特别是在需要处理 NaN+0/-0 的情况下。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
umi 是一个基于 React 的企业级前端应用开发框架,可以帮助开发者快速构建高质量的单页应用或多页应用。使用 umi 时,可以通过 useModel 来使用 umi 中的模型。 useModel 是 umi 中用于访问和管理模型的 Hook,可以在函数组件中使用。在使用 useModel 之前,需要先在 model 文件夹中定义模型。 具体使用方法如下: 1. 在函数组件中引入 useModel ``` import { useModel } from 'umi'; ``` 2. 使用 useModel 获取模型对象 ``` const model = useModel('modelName'); ``` 其中,modelName 是在 model 文件夹中定义的模型名称。 3. 在组件中使用模型 ``` function MyComponent() { const model = useModel('modelName'); const { data, error, loading } = model.someEffect(); // ... } ``` 在组件中,可以通过模型对象调用模型中的方法,例如一些异步操作。 注意:在使用 useModel 之前,需要先在 config/config.ts 文件中配置 model 属性,指定 model 文件夹的路径。 ``` export default { // ... // 配置 model 文件夹的路径 // 即 model 文件夹所在的目录 // 默认为 src/models // 如果在根目录下创建了 models 目录,则需要手动指定路径为 ./models // 如果没有使用 model,则可以省略该属性 // 例如:model: {}, model: { // 指定 model 文件夹的路径 // 如果 models 目录在根目录下,则为 './models' // 如果 models 目录在 src 目录下,则为 './src/models' // 如果没有使用 model,则可以省略该属性 // 例如:'./models', dir: 'model', }, // ... }; ``` 以上就是使用 umi 中的 useModel Hook 的基本方法,希望能帮助到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值