JS设计模式在React-hook中的实践

JavaScript设计模式是一组被广泛使用的软件设计解决方案,用于解决代码复杂性、可维护性、重用性和扩展性等方面的问题。本文将介绍常见的JavaScript设计模式,并使用React-Hook对每种设计模式进行实践来帮助大家理解。

1. 单例模式

单例模式是一种创建型模式,用于确保类只有一个实例,并提供全局访问点。在JavaScript中,可以通过将对象字面量封装在函数闭包中来实现单例模式。

const Singleton = (function() {
  let instance;
  
  function createInstance() {
    const object = { name: 'John' };
    return object;
  }
  
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

console.log(Singleton.getInstance()); // { name: 'John' }
console.log(Singleton.getInstance()); // { name: 'John' }

在React中,单例模式可以用于创建共享状态的上下文对象。例如,以下代码演示如何使用React Hook创建一个计数器上下文对象。

import { createContext, useContext, useState } from 'react';

const CounterContext = createContext(null);

export const useCounter = () => useContext(CounterContext);

export const CounterProvider = ({ children }) => {
  const [count, setCount] = useState(0);
  
  return (
    <CounterContext.Provider value={{ count, setCount }}>
      {children}
    </CounterContext.Provider>
  );
};

使用上述代码,我们可以在React应用程序中使用useCounter钩子来访问计数器状态。

2. 观察者模式

观察者模式是一种行为型模式,用于在对象之间建立一对多的依赖关系,当一个对象状态发生变化时,所有依赖该对象的对象都会被通知。在JavaScript中,可以使用回调函数或事件触发器来实现观察者模式。

class Subject {
  constructor() {
    this.observers = [];
  }
  
  attach(observer) {
    this.observers.push(observer);
  }
  
  detach(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
  
  notify() {
    this.observers.forEach(observer => observer.update());
  }
}

class Observer {
  update() {
    console.log('The subject has been updated');
  }
}

const subject = new Subject();
const observer = new Observer();

subject.attach(observer);
subject.notify(); // The subject has been updated
subject.detach(observer);

在React中,观察者模式可以用于处理组件之间的状态同步。例如,以下代码演示如何使用React Hook创建一个计数器组件,当计数器值发生变化时,通知所有订阅组件。

import { createContext, useContext, useState, useEffect } from 'react';

const CounterContext = createContext(null);

export const useCounter = () => useContext(CounterContext);

export const CounterProvider = ({ children }) => {
  const [count, setCount] = useState(0);
  const [subscribers, setSubscribers] = useState([]);

  useEffect(() => {
    subscribers.forEach(subscriber => subscriber());
  }, [count, subscribers]);

  const subscribe = callback => {
    setSubscribers(subscribers => [...subscribers, callback]);
  };

  const unsubscribe = callback => {
    setSubscribers(subscribers =>
      subscribers.filter(subscriber => subscriber !== callback),
    );
  };

  return (
    <CounterContext.Provider
      value={{ count, setCount, subscribe, unsubscribe }}
    >
      {children}
    </CounterContext.Provider>
  );
};

export const CounterDisplay = () => {
  const { count, subscribe, unsubscribe } = useCounter();

  useEffect(() => {
    const callback = () => console.log(`The count is ${count}`);
    subscribe(callback);
    return () => unsubscribe(callback);
  }, [count, subscribe, unsubscribe]);

  return <p>Count: {count}</p>;
};

export const CounterButton = () => {
  const { count, setCount } = useCounter();

  const handleClick = () => setCount(count => count + 1);

  return <button onClick={handleClick}>Increment</button>;
};


使用上述代码,我们可以在React应用程序中创建一个计数器上下文,同时使用CounterDisplayCounterButton组件订阅该上下文。每次计数器值发生变化时,所有订阅组件都会得到通知。

3. 工厂模式

工厂模式是一种创建型模式,用于封装对象的实例化过程,从而避免直接使用new操作符创建对象。在JavaScript中,可以使用工厂函数或构造函数来实现工厂模式。

class Product {
  constructor(name) {
    this.name = name;
  }
  
  getName() {
    return this.name;
  }
}

class ProductFactory {
  create(name) {
    return new Product(name);
  }
}

const factory = new ProductFactory();
const product = factory.create('Apple');
console.log(product.getName()); // Apple

在React中,工厂模式可以用于创建具有相同结构的组件。例如,以下代码演示如何使用React Hook创建一个可重用的按钮工厂函数。

import React from 'react';

const Button = ({ text, onClick }) => (
  <button onClick={onClick}>{text}</button>
);

export const createButton = ({ text, onClick }) => (
  <Button text={text} onClick={onClick} />
);

使用上述代码,我们可以在React应用程序中使用createButton工厂函数创建任意数量的按钮组件,每个按钮组件都具有相同的结构和属性。

import React from 'react';
import { createButton } from './ButtonFactory';

const IncrementButton = createButton({
  text: 'Increment',
  onClick: () => console.log('Increment')
});

const DecrementButton = createButton({
  text: 'Decrement',
  onClick: () => console.log('Decrement')
});

const App = () => (
  <>
    <IncrementButton />
    <DecrementButton />
  </>
);

4. 适配器模式

适配器模式是一种结构型模式,用于将一个接口转换为另一个接口,以满足不同的客户端需求。在JavaScript中,可以使用对象适配器或类适配器来实现适配器模式。

class Adaptee {
  specificRequest() {
    return 'Specific request';
  }
}

class Target {
  request() {
    return 'Default request';
  }
}

class Adapter extends Target {
  constructor(adaptee) {
    super();
    this.adaptee = adaptee;
  }
  
  request() {
    return this.adaptee.specificRequest();
  }
}

const target = new Target();
console.log(target.request()); // Default request

const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
console.log(adapter.request()); // Specific request

在React中,适配器模式可以用于将不同类型的数据转换为React组件所需的数据格式。例如,以下代码演示如何使用React Hook创建一个数据适配器。

import React, { useState, useEffect } from 'react';

const fetchData = async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  const data = await response.json();
  return data;
};

const adaptData = data =>
  data.map(user => ({ id: user.id, name: user.name, email: user.email }));

export const useUsers = () => {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    fetchData().then(data => setUsers(adaptData(data)));
  }, []);
  
  return users;
};

使用上述代码,我们可以在React应用程序中使用useUsers自定义Hook获取用户数据,该Hook将从API中获取的用户数据转换为React组件所需的格式。

import React from 'react';
import { useUsers } from './UserAdapter';

const UserList = () => {
  const users = useUsers();
  
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name} ({user.email})</li>
      ))}
    </ul>
  );
};

5. 观察者模式

观察者模式是一种行为型模式,用于建立对象之间的一对多依赖关系,当一个对象状态发生变化时,它的所有依赖对象都会收到通知。在JavaScript中,可以使用发布-订阅模式或回调函数来实现观察者模式。

class Subject {
  constructor() {
    this.observers = [];
  }
  
  attach(observer) {
    this.observers.push(observer);
  }
  
  detach(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }
  
  notify() {
    this.observers.forEach(observer => observer.update());
  }
}

class Observer {
  constructor(subject) {
    this.subject = subject;
    this.subject.attach(this);
  }
  
  update() {
    console.log('Observer updated');
  }
  
  detach() {
    this.subject.detach(this);
  }
}

const subject = new Subject();
const observer1 = new Observer(subject);
const observer2 = new Observer(subject);

subject.notify(); // Observer updated Observer updated
observer1.detach();
subject.notify(); // Observer updated

在React中,观察者模式可以用于在组件之间共享状态并响应状态变化。以下是使用React Hook实现观察者模式的示例代码:

import React, { createContext, useContext, useState } from 'react';

const ObserverContext = createContext(null);

export const ObserverProvider = ({ children }) => {
  const [state, setState] = useState({});
  
  const attach = observer => {
    setState({ ...state, [observer.id]: observer });
  };
  
  const detach = observer => {
    const { [observer.id]: omit, ...rest } = state;
    setState(rest);
  };
  
  const notify = () => {
    Object.values(state).forEach(observer => observer.update());
  };
  
  const value = { attach, detach, notify };
  
  return (
    <ObserverContext.Provider value={value}>
      {children}
    </ObserverContext.Provider>
  );
};

export const useObserver = update => {
  const { attach, detach, notify } = useContext(ObserverContext);
  const [id] = useState(Math.random().toString());
  
  const observer = { id, update };
  
  attach(observer);
  
  useEffect(() => {
    return () => detach(observer);
  }, [observer]);
  
  return notify;
};

使用上述代码,我们可以在React应用程序中使用ObserverProvider组件来创建一个全局的观察者容器,该容器可以用于存储和管理所有观察者对象。然后,我们可以使用useObserver自定义Hook在React组件中创建一个观察者,并在需要时调用notify函数来通知所有观察者。

import React from 'react';
import { ObserverProvider, useObserver } from './Observer';

const Counter = () => {
  const [count, setCount] = useState(0);
  const notify = useObserver(() => setCount(count + 1));
  
  const handleClick = () => {
    notify();
  };
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
};

const App = () => {
  return (
    <ObserverProvider>
      <Counter />
      <Counter />
    </ObserverProvider>
  );
};

在上面的示例中,我们使用ObserverProvider组件来创建一个全局的观察者容器,并在Counter组件中使用useObserver自定义Hook来创建一个观察者,该观察者会在每次单击按钮时通知所有观察者更新计数器的状态。由于Counter组件在ObserverProvider组件的下方调用了两次,因此在单击按钮时,所有计数器都将更新其状态并显示新的计数值。

原文链接:https://juejin.cn/post/7225879731178356791 作者:J3LLYPUNK

(0)
上一篇 2023年4月25日 上午11:12
下一篇 2023年4月26日 上午10:05

相关推荐

发表回复

登录后才能评论