阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > React中的设计模式实践

React中的设计模式实践

作者:陈川 阅读数:26530人阅读 分类: JavaScript

React作为现代前端开发的主流框架,其组件化思想与设计模式天然契合。合理运用设计模式能提升代码的可维护性、复用性和扩展性,尤其在复杂应用场景下效果显著。以下是React中常见设计模式的实践解析。

组合模式(Compound Components)

组合模式通过组件嵌套实现功能解耦,典型例子是<select><option>的关系。在React中,可以通过React.ContextReact.cloneElement实现:

const Tabs = ({ children }) => {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <TabContext.Provider value={{ activeIndex, setActiveIndex }}>
      <div className="tabs">{children}</div>
    </TabContext.Provider>
  );
};

const TabList = ({ children }) => {
  const { activeIndex, setActiveIndex } = useContext(TabContext);
  return React.Children.map(children, (child, index) =>
    React.cloneElement(child, {
      isActive: index === activeIndex,
      onClick: () => setActiveIndex(index)
    })
  );
};

// 使用示例
<Tabs>
  <TabList>
    <Tab>首页</Tab>
    <Tab>产品</Tab>
  </TabList>
  <TabPanels>
    <TabPanel>首页内容</TabPanel>
    <TabPanel>产品列表</TabPanel>
  </TabPanels>
</Tabs>

这种模式让父组件管理状态,子组件通过上下文获取状态,保持UI与逻辑分离。

渲染属性模式(Render Props)

通过函数作为子组件(children prop)传递动态内容,实现逻辑复用:

const MouseTracker = ({ children }) => {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const handleMouseMove = (e) => setPosition({ x: e.clientX, y: e.clientY });

  return (
    <div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
      {children(position)}
    </div>
  );
};

// 使用示例
<MouseTracker>
  ({ x, y }) => (
    <h1>
      鼠标位置: {x}, {y}
    </h1>
  )
</MouseTracker>

更灵活的变体是使用任意prop名称:

<DataProvider render={(data) => <Child data={data} />} />

高阶组件(HOC)

通过函数包装组件实现横切关注点:

const withLoading = (WrappedComponent) => {
  return (props) => {
    const [loading, setLoading] = useState(true);
    useEffect(() => {
      const timer = setTimeout(() => setLoading(false), 1000);
      return () => clearTimeout(timer);
    }, []);

    return loading ? <Spinner /> : <WrappedComponent {...props} />;
  };
};

// 使用示例
const UserProfile = ({ user }) => <div>{user.name}</div>;
const UserProfileWithLoading = withLoading(UserProfile);

注意HOC可能导致ref丢失,需要使用React.forwardRef解决。

状态提升与单一数据源

在表单组件中常见状态提升模式:

const FormContainer = () => {
  const [formData, setFormData] = useState({
    username: '',
    password: ''
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };

  return (
    <form>
      <Input 
        name="username"
        value={formData.username}
        onChange={handleChange}
      />
      <Input
        name="password"
        type="password"
        value={formData.password}
        onChange={handleChange}
      />
    </form>
  );
};

提供者模式(Provider Pattern)

通过Context API实现全局状态管理:

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

const App = () => {
  const [theme, setTheme] = useState('dark');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Toolbar />
    </ThemeContext.Provider>
  );
};

const ThemedButton = () => {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <button 
      style={{ background: theme === 'dark' ? '#333' : '#EEE' }}
      onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
    >
      切换主题
    </button>
  );
};

容器/展示组件模式

分离逻辑与UI的经典模式:

// 容器组件
const UserListContainer = () => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    fetchUsers().then(data => {
      setUsers(data);
      setLoading(false);
    });
  }, []);

  return <UserList users={users} loading={loading} />;
};

// 展示组件
const UserList = ({ users, loading }) => (
  loading ? <Spinner /> : (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
);

自定义Hook模式

将组件逻辑提取为可复用Hook:

const useLocalStorage = (key, initialValue) => {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
};

// 使用示例
const Counter = () => {
  const [count, setCount] = useLocalStorage('counter', 0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      点击次数: {count}
    </button>
  );
};

策略模式在表单验证中的应用

动态切换验证规则:

const validationStrategies = {
  email: (value) => /^\S+@\S+$/.test(value),
  password: (value) => value.length >= 8
};

const useValidation = (strategyName) => {
  const validate = validationStrategies[strategyName];
  const [error, setError] = useState('');

  const check = (value) => {
    const isValid = validate(value);
    setError(isValid ? '' : `Invalid ${strategyName}`);
    return isValid;
  };

  return [error, check];
};

// 使用示例
const EmailInput = () => {
  const [email, setEmail] = useState('');
  const [error, validateEmail] = useValidation('email');

  return (
    <>
      <input
        type="text"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        onBlur={() => validateEmail(email)}
      />
      {error && <div className="error">{error}</div>}
    </>
  );
};

观察者模式与事件总线

实现组件间松耦合通信:

const EventBus = {
  events: {},
  subscribe(event, callback) {
    if (!this.events[event]) this.events[event] = [];
    this.events[event].push(callback);
  },
  publish(event, data) {
    (this.events[event] || []).forEach(cb => cb(data));
  }
};

// 发布者组件
const Publisher = () => (
  <button onClick={() => EventBus.publish('alert', '按钮被点击')}>
    触发事件
  </button>
);

// 订阅者组件
const Subscriber = () => {
  const [message, setMessage] = useState('');
  
  useEffect(() => {
    EventBus.subscribe('alert', setMessage);
    return () => EventBus.unsubscribe('alert', setMessage);
  }, []);

  return <div>{message}</div>;
};

工厂模式创建动态组件

根据配置生成不同组件:

const componentMap = {
  text: ({ value }) => <span>{value}</span>,
  image: ({ src }) => <img src={src} alt="" />,
  button: ({ label, onClick }) => (
    <button onClick={onClick}>{label}</button>
  )
};

const DynamicComponent = ({ type, ...props }) => {
  const Component = componentMap[type] || componentMap.text;
  return <Component {...props} />;
};

// 使用示例
const configs = [
  { type: 'text', value: '静态文本' },
  { type: 'button', label: '提交', onClick: () => alert('点击') }
];

const ComponentFactory = () => (
  <div>
    {configs.map((config, i) => (
      <DynamicComponent key={i} {...config} />
    ))}
  </div>
);

备忘录模式与状态快照

实现撤销/重做功能:

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

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

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

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

// 使用示例
const SketchPad = () => {
  const [paths, setPaths, { undo, redo }] = useHistory([]);
  
  const handleDraw = (newPath) => {
    setPaths([...paths, newPath]);
  };

  return (
    <div>
      <Canvas onDraw={handleDraw} paths={paths} />
      <button onClick={undo} disabled={!canUndo}>撤销</button>
      <button onClick={redo} disabled={!canRedo}>重做</button>
    </div>
  );
};

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌