您现在的位置是:网站首页 > React中的设计模式实践文章详情

React中的设计模式实践

React作为现代前端开发的主流框架,其灵活性和组件化思想为设计模式的应用提供了广阔空间。合理运用设计模式能提升代码的可维护性、复用性和扩展性,尤其在复杂应用场景中效果显著。

组件组合模式

组合模式在React中体现为组件树的嵌套结构。通过将小组件组合成更大组件,可以构建复杂的UI层次。典型的例子是表单组件的封装:

const Input = ({ label, ...props }) => (
  <div className="form-group">
    <label>{label}</label>
    <input {...props} />
  </div>
);

const Form = ({ children }) => (
  <form className="form-container">
    {children}
    <button type="submit">提交</button>
  </form>
);

// 使用组合
<Form>
  <Input label="用户名" name="username" />
  <Input label="密码" type="password" name="password" />
</Form>

这种模式的优势在于:

  • 每个组件只关注单一职责
  • 通过props.children实现灵活的内容注入
  • 组件之间解耦,便于独立测试和维护

高阶组件模式

高阶组件(HOC)是React中实现横切关注点的经典方式。它接收组件作为参数并返回增强后的新组件:

function withLoading(Component) {
  return function EnhancedComponent({ isLoading, ...props }) {
    return isLoading ? (
      <div className="loader">加载中...</div>
    ) : (
      <Component {...props} />
    );
  };
}

// 使用HOC增强组件
const EnhancedTable = withLoading(DataTable);

// 渲染时
<EnhancedTable isLoading={true} data={data} />

常见HOC应用场景包括:

  • 权限控制
  • 数据获取
  • 错误边界处理
  • 性能监控

渲染属性模式

Render Props通过函数prop共享组件逻辑,比HOC更具灵活性:

class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };

  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// 使用渲染属性
<MouseTracker
  render={({ x, y }) => (
    <h1>
      鼠标位置: {x}, {y}
    </h1>
  )}
/>

这种模式的优点在于:

  • 逻辑与UI完全解耦
  • 避免HOC的组件嵌套问题
  • 动态组合行为更灵活

状态管理中的观察者模式

在全局状态管理中,观察者模式是Redux等库的核心:

// 创建store
const store = createStore(reducer);

// 订阅状态变化
const unsubscribe = store.subscribe(() => {
  console.log('状态变更:', store.getState());
});

// 派发action
store.dispatch({ type: 'INCREMENT' });

// React组件连接
const Counter = connect(
  state => ({ count: state.count }),
  dispatch => ({
    increment: () => dispatch({ type: 'INCREMENT' })
  })
)(function({ count, increment }) {
  return <button onClick={increment}>{count}</button>;
});

观察者模式在React中的关键点:

  • 单一数据源原则
  • 状态变更通过订阅/通知机制
  • 避免直接修改状态

容器/展示组件模式

这种模式将组件分为两类:

  • 容器组件:管理状态和逻辑
  • 展示组件:负责UI渲染
// 展示组件
const UserList = ({ users, onDelete }) => (
  <ul>
    {users.map(user => (
      <li key={user.id}>
        {user.name}
        <button onClick={() => onDelete(user.id)}>删除</button>
      </li>
    ))}
  </ul>
);

// 容器组件
class UserListContainer extends React.Component {
  state = { users: [] };

  componentDidMount() {
    fetch('/api/users')
      .then(res => res.json())
      .then(users => this.setState({ users }));
  }

  handleDelete = (id) => {
    this.setState(prev => ({
      users: prev.users.filter(user => user.id !== id)
    }));
  };

  render() {
    return <UserList 
      users={this.state.users} 
      onDelete={this.handleDelete} 
    />;
  }
}

这种分离带来的好处:

  • 业务逻辑与UI解耦
  • 展示组件可复用性高
  • 测试更简单(展示组件只需快照测试)

工厂模式创建组件

工厂模式可用于动态创建不同类型的组件:

const Button = ({ type, ...props }) => {
  const components = {
    primary: PrimaryButton,
    danger: DangerButton,
    default: DefaultButton
  };
  
  const Component = components[type] || components.default;
  return <Component {...props} />;
};

// 使用工厂
<Button type="primary" onClick={handleClick}>
  主要按钮
</Button>

适用场景包括:

  • 需要根据配置动态渲染组件
  • 统一管理相似组件的创建逻辑
  • 简化复杂条件渲染

策略模式处理算法

策略模式允许在运行时选择算法:

const validationStrategies = {
  email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
  phone: value => /^1[3-9]\d{9}$/.test(value),
  required: value => value.trim() !== ''
};

const FormField = ({ type, value, onValidate }) => {
  const validate = () => {
    const isValid = validationStrategies[type](value);
    onValidate(isValid);
  };

  return (
    <div>
      <input 
        type={type} 
        value={value} 
        onChange={e => onValidate(validationStrategies[type](e.target.value))}
      />
    </div>
  );
};

策略模式的优势:

  • 避免复杂的条件语句
  • 算法可独立变化和扩展
  • 便于单元测试

装饰器模式增强功能

虽然React不直接支持装饰器语法,但可以实现类似效果:

function withLogging(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log(`组件 ${WrappedComponent.name} 已挂载`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

// 使用装饰器
@withLogging
class MyComponent extends React.Component {
  render() {
    return <div>装饰器示例</div>;
  }
}

装饰器常用于:

  • 日志记录
  • 性能监测
  • 异常捕获
  • 权限校验

依赖注入模式

通过Context API实现依赖注入:

const ServicesContext = React.createContext();

const ApiService = {
  fetchData: () => Promise.resolve([1, 2, 3])
};

const App = () => (
  <ServicesContext.Provider value={ApiService}>
    <DataConsumer />
  </ServicesContext.Provider>
);

const DataConsumer = () => {
  const { fetchData } = useContext(ServicesContext);
  const [data, setData] = useState([]);

  useEffect(() => {
    fetchData().then(setData);
  }, [fetchData]);

  return <div>{data.join(', ')}</div>;
};

依赖注入的好处:

  • 降低组件间的直接依赖
  • 便于模拟服务进行测试
  • 运行时动态替换实现

备忘录模式实现撤销

备忘录模式可用于实现状态历史记录:

function useUndo(initialState) {
  const [history, setHistory] = useState([initialState]);
  const [index, setIndex] = useState(0);

  const current = history[index];
  
  const setState = (newState) => {
    const newHistory = history.slice(0, index + 1);
    setHistory([...newHistory, newState]);
    setIndex(newHistory.length);
  };

  const undo = () => index > 0 && setIndex(index - 1);
  const redo = () => index < history.length - 1 && setIndex(index + 1);

  return [current, setState, { undo, redo, canUndo: index > 0, canRedo: index < history.length - 1 }];
}

// 使用示例
const [text, setText, { undo, redo }] = useUndo('');

享元模式优化性能

享元模式通过共享相似对象减少内存使用:

const FlyweightIcon = React.memo(({ type }) => {
  const icons = {
    success: <SuccessIcon />,
    error: <ErrorIcon />,
    warning: <WarningIcon />
  };
  
  return icons[type];
});

// 大量重复使用
{items.map(item => (
  <FlyweightIcon key={item.id} type={item.status} />
))}

命令模式封装操作

命令模式将操作封装为对象:

class Calculator {
  constructor() {
    this.current = 0;
    this.history = [];
  }

  execute(command) {
    this.current = command.execute(this.current);
    this.history.push(command);
  }

  undo() {
    const command = this.history.pop();
    this.current = command.undo(this.current);
  }
}

class AddCommand {
  constructor(value) {
    this.value = value;
  }

  execute(current) {
    return current + this.value;
  }

  undo(current) {
    return current - this.value;
  }
}

// React组件中使用
const CalcUI = () => {
  const [calc] = useState(new Calculator());
  const [result, setResult] = useState(0);

  const handleAdd = (value) => {
    calc.execute(new AddCommand(value));
    setResult(calc.current);
  };

  const handleUndo = () => {
    calc.undo();
    setResult(calc.current);
  };

  return (
    <div>
      <div>结果: {result}</div>
      <button onClick={() => handleAdd(5)}>加5</button>
      <button onClick={handleUndo}>撤销</button>
    </div>
  );
};

模板方法模式定义算法骨架

模板方法模式在React中可用于定义组件生命周期:

abstract class BaseComponent extends React.Component {
  // 模板方法
  componentDidMount() {
    this.init();
    this.fetchData();
    this.setupListeners();
  }

  abstract init(): void;
  abstract fetchData(): void;
  abstract setupListeners(): void;
}

class ConcreteComponent extends BaseComponent {
  init() {
    console.log('初始化...');
  }

  fetchData() {
    console.log('获取数据...');
  }

  setupListeners() {
    console.log('设置监听...');
  }
}

空对象模式处理默认情况

空对象模式可避免null检查:

const NullUser = {
  name: '访客',
  isAuthenticated: false,
  hasPermission: () => false
};

const UserGreeting = ({ user = NullUser }) => (
  <div>
    {user.isAuthenticated ? (
      <h1>欢迎回来, {user.name}!</h1>
    ) : (
      <h1>请登录</h1>
    )}
  </div>
);

// 使用时不需担心user为null
<UserGreeting />

中介者模式协调组件通信

中介者模式简化组件间通信:

const ChatMediator = {
  participants: [],

  register(participant) {
    this.participants.push(participant);
  },

  send(message, sender) {
    this.participants.forEach(participant => {
      if (participant !== sender) {
        participant.receive(message);
      }
    });
  }
};

class User extends React.Component {
  state = { messages: [] };

  componentDidMount() {
    ChatMediator.register(this);
  }

  receive = (message) => {
    this.setState(prev => ({
      messages: [...prev.messages, message]
    }));
  };

  send = () => {
    ChatMediator.send(this.props.name + ': ' + this.inputRef.value, this);
  };

  render() {
    return (
      <div>
        <div>{this.state.messages.join('\n')}</div>
        <input ref={ref => this.inputRef = ref} />
        <button onClick={this.send}>发送</button>
      </div>
    );
  }
}

状态模式管理复杂状态

状态模式简化状态转换逻辑:

class TrafficLightState {
  constructor(light) {
    this.light = light;
  }

  change() {}
}

class RedState extends TrafficLightState {
  change() {
    console.log('红灯 -> 绿灯');
    this.light.setState(new GreenState(this.light));
  }
}

class GreenState extends TrafficLightState {
  change() {
    console.log('绿灯 -> 黄灯');
    this.light.setState(new YellowState(this.light));
  }
}

class TrafficLight extends React.Component {
  state = { currentState: new RedState(this) };

  setStateClass(state) {
    this.setState({ currentState: state });
  }

  handleChange = () => {
    this.state.currentState.change();
  };

  render() {
    return (
      <div>
        <button onClick={this.handleChange}>切换信号灯</button>
      </div>
    );
  }
}

访问者模式处理复杂数据结构

访问者模式适合处理异构元素集合:

class ElementVisitor {
  visit(element) {
    console.log('默认访问者');
  }
}

class JSONVisitor extends ElementVisitor {
  visit(element) {
    return JSON.stringify(element);
  }
}

class ReactElement {
  accept(visitor) {
    return visitor.visit(this);
  }
}

class UserElement extends ReactElement {
  constructor(props) {
    super();
    this.props = props;
  }
}

// 使用访问者
const user = new UserElement({ name: '张三', age: 30 });
const json = user.accept(new JSONVisitor());
console.log(json); // 输出JSON字符串

责任链模式处理请求

责任链模式允许多个对象处理请求:

class Handler {
  constructor(successor = null) {
    this.successor = successor;
  }

  handle(request) {
    if (this.successor) {
      return this.successor.handle(request);
    }
    return null;
  }
}

class AuthHandler extends Handler {
  handle(request) {
    if (request.user?.isAuthenticated) {
      return super.handle(request);
    }
    throw new Error('未授权');
  }
}

class LoggingHandler extends Handler {
  handle(request) {
    console.log('请求:', request);
    return super.handle(request);
  }
}

// React组件中使用
const ProtectedComponent = () => {
  const handlerChain = new AuthHandler(new LoggingHandler());

  const handleAction = () => {
    try {
      handlerChain.handle({ user: { isAuthenticated: true } });
      // 执行业务逻辑
    } catch (error) {
      console.error(error);
    }
  };

  return <button onClick={handleAction}>执行操作</button>;
};

桥接模式分离抽象与实现

桥接模式连接不同维度的变化:

// 实现部分
class Renderer {
  render(shape) {}
}

class SVGRenderer extends Renderer {
  render(shape) {
    return `<svg>${shape.draw()}</svg>`;
  }
}

// 抽象部分
class Shape {
  constructor(renderer) {
    this.renderer = renderer;
  }

  draw() {}
}

class Circle extends Shape {
  draw() {
    return this.renderer.render(this);
  }
}

// React中使用
const ShapeRenderer = ({ type }) => {
  const renderer = new SVGRenderer();
  const shape = type === 'circle' ? new Circle(renderer) : null;
  
  return <div dangerouslySetInnerHTML={{ __html: shape?.draw() }} />;
};

原型模式克隆对象

原型模式在React中可用于状态复制:

const initialFormState = {
  values: {},
  errors: {},
  isSubmitting: false
};

function useForm(initialState = initialFormState) {
  const [state, setState] = useState(initialState);

  const reset = () => {
    // 使用对象展开实现浅克隆
    setState({ ...initialFormState });
  };

  const cloneState = () => {
    // 深克隆实现
    return JSON.parse(JSON.stringify(state));
  };

  return { state, setState, reset, cloneState };
}

对象池模式管理资源

对象池模式优化频繁创建销毁的对象:

class TooltipPool {
  constructor() {
    this.pool = [];
  }

  get() {
    return this.pool.pop() || document.createElement('div');
  }

  release(tooltip) {
    tooltip.style.display = 'none';
    this.pool.push(tooltip);
  }
}

// React组件中使用
const Tooltip = ({ text }) => {
  const pool = useRef(new TooltipPool());
  const [tooltip, setTooltip] = useState(null);

  useEffect(() => {
    const element = pool.current.get();
    element.textContent = text;
    element.style.display = 'block';
    setTooltip(element);

    return () => {
      pool.current.release(element);
    };
  }, [text]);

  return <div ref={ref => ref?.appendChild(tooltip)} />;
};

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步