阿里云主机折上折
  • 微信号
Current Site:Index > No rendering optimization (100 'setState' calls per second)

No rendering optimization (100 'setState' calls per second)

Author:Chuan Chen 阅读数:22351人阅读 分类: 前端综合

The Joy of 'setState' 100 Times Per Second

In front-end development, performance optimization is often seen as a burden. But if we think differently, not optimizing rendering can bring unexpected fun. By frantically calling setState, we can create astonishing lag effects, giving users a true "digital rollercoaster" experience.

Basic Principle: Make React Work Like Crazy

React's virtual DOM and diff algorithm are designed to improve performance, but we can easily overload it. The key is to continuously trigger component updates:

class ChaosComponent extends React.Component {
  state = { counter: 0 };

  componentDidMount() {
    // Update state every 10 milliseconds
    this.interval = setInterval(() => {
      this.setState({ counter: this.state.counter + 1 });
    }, 10);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    // Render a complex DOM structure
    return (
      <div>
        {Array(100).fill().map((_, i) => (
          <div key={i}>
            <p>Counter value: {this.state.counter}</p>
            <div style={{ 
              width: `${Math.abs(Math.sin(this.state.counter/10)) * 100}%`,
              height: '20px',
              background: `hsl(${this.state.counter % 360}, 100%, 50%)`
            }} />
          </div>
        ))}
      </div>
    );
  }
}

Advanced Technique: Nested Updates

A single component might not be exciting enough; we can create chain reactions between components:

class ParentComponent extends React.Component {
  state = { value: 0 };

  componentDidMount() {
    setInterval(() => {
      this.setState({ value: Math.random() });
    }, 16); // ~60fps
  }

  render() {
    return (
      <div>
        <ChildA data={this.state.value} />
        <ChildB data={this.state.value * 2} />
      </div>
    );
  }
}

class ChildA extends React.Component {
  state = { derived: 0 };

  componentDidUpdate(prevProps) {
    if (this.props.data !== prevProps.data) {
      this.setState({ derived: this.props.data * 3 });
    }
  }

  render() {
    return <GrandChild data={this.state.derived} />;
  }
}

class ChildB extends React.Component {
  // Similar chain update logic
}

Ultimate Destruction: Global State Storm

When local components aren't enough, we can introduce global state management tools to expand the impact:

// store.js
let timer;
const store = {
  state: { value: 0 },
  subscribers: new Set(),
  startChaos() {
    timer = setInterval(() => {
      this.state.value += 0.1;
      this.subscribers.forEach(cb => cb());
    }, 10);
  },
  stopChaos() {
    clearInterval(timer);
  },
  subscribe(cb) {
    this.subscribers.add(cb);
    return () => this.subscribers.delete(cb);
  }
};

// ConnectedComponent.jsx
class ConnectedComponent extends React.Component {
  state = { storeValue: store.state.value };

  componentDidMount() {
    this.unsubscribe = store.subscribe(() => {
      this.setState({ storeValue: store.state.value });
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    return <div>{this.state.storeValue.toFixed(2)}</div>;
  }
}

// Start chaos at the app entry
store.startChaos();

Visual Impact: Combining CSS Animations with State Updates

To make the lag more noticeable, we can combine state updates with CSS animations:

class VisualChaos extends React.Component {
  state = { 
    angle: 0,
    scale: 1,
    color: '#ff0000'
  };

  componentDidMount() {
    setInterval(() => {
      this.setState({
        angle: (this.state.angle + 5) % 360,
        scale: 0.5 + Math.random(),
        color: `hsl(${Math.random() * 360}, 100%, 50%)`
      });
    }, 16);
  }

  render() {
    const style = {
      transform: `rotate(${this.state.angle}deg) scale(${this.state.scale})`,
      backgroundColor: this.state.color,
      width: '100px',
      height: '100px',
      transition: 'all 0.1s ease'
    };

    return <div style={style} />;
  }
}

Event Handling: Chain Reactions from User Interaction

Make every user action trigger massive updates:

class InteractiveChaos extends React.Component {
  state = { clicks: 0 };

  handleClick = () => {
    // One click triggers 100 state updates
    for (let i = 0; i < 100; i++) {
      setTimeout(() => {
        this.setState(prev => ({ clicks: prev.clicks + 1 }));
      }, i * 10);
    }
  };

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me! Clicks: {this.state.clicks}
      </button>
    );
  }
}

Data Fetching: Infinite Request Loop

Combine API requests to create a more comprehensive performance disaster:

class ApiChaos extends React.Component {
  state = { data: null, loading: false };

  fetchData = () => {
    this.setState({ loading: true }, () => {
      fetch('/api/data')
        .then(res => res.json())
        .then(data => {
          this.setState({ data, loading: false }, () => {
            // Request again immediately after data arrives
            setTimeout(this.fetchData, 100);
          });
        });
    });
  };

  componentDidMount() {
    this.fetchData();
  }

  render() {
    return this.state.loading ? 
      <div>Loading...</div> : 
      <div>{JSON.stringify(this.state.data)}</div>;
  }
}

Memory Leaks: Components That Never Unmount

Ensure components continue working even after being removed from the DOM:

class ZombieComponent extends React.Component {
  state = { counter: 0 };

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState({ counter: this.state.counter + 1 });
    }, 100);
  }

  // Intentionally don't implement componentWillUnmount
  // Let the timer keep running after unmount

  render() {
    return <div>Counter: {this.state.counter}</div>;
  }
}

// Usage: Frequent mounting/unmounting
class App extends React.Component {
  state = { show: true };

  toggle = () => {
    this.setState(prev => ({ show: !prev.show }));
  };

  render() {
    return (
      <div>
        <button onClick={this.toggle}>Toggle</button>
        {this.state.show && <ZombieComponent />}
      </div>
    );
  }
}

Large Lists: Render All Data

Ignore virtual scrolling and directly render massive amounts of data:

class BigDataRenderer extends React.Component {
  state = { items: [] };

  componentDidMount() {
    fetch('/api/huge-list')
      .then(res => res.json())
      .then(items => {
        this.setState({ items });
        
        // Continuously add new data
        setInterval(() => {
          this.setState(prev => ({
            items: [...prev.items, {
              id: Date.now(),
              content: Math.random().toString(36).substring(2)
            }]
          }));
        }, 100);
      });
  }

  render() {
    return (
      <ul>
        {this.state.items.map(item => (
          <li key={item.id}>
            <ComplexListItem data={item} />
          </li>
        ))}
      </ul>
    );
  }
}

// Each list item is a complex component
class ComplexListItem extends React.Component {
  state = { expanded: false };

  toggle = () => {
    this.setState(prev => ({ expanded: !prev.expanded }));
  };

  render() {
    return (
      <div>
        <button onClick={this.toggle}>
          {this.props.data.content}
        </button>
        {this.state.expanded && (
          <div>
            {Array(10).fill().map((_, i) => (
              <p key={i}>Details {i}</p>
            ))}
          </div>
        )}
      </div>
    );
  }
}

Context Abuse: Deeply Nested Updates

Use React context to propagate updates throughout the entire app:

const ChaosContext = React.createContext();

class ChaosProvider extends React.Component {
  state = { value: 0 };

  componentDidMount() {
    setInterval(() => {
      this.setState({ value: this.state.value + 1 });
    }, 10);
  }

  render() {
    return (
      <ChaosContext.Provider value={this.state.value}>
        {this.props.children}
      </ChaosContext.Provider>
    );
  }
}

// Use at the top level of the app
const App = () => (
  <ChaosProvider>
    <Header />
    <MainContent />
    <Footer />
  </ChaosProvider>
);

// Any component consuming context will update frequently
const SomeComponent = () => (
  <ChaosContext.Consumer>
    {value => <div>Context value: {value}</div>}
  </ChaosContext.Consumer>
);

Animation Frames: requestAnimationFrame Abuse

Use the browser's animation frame mechanism to create smoother lag:

class SmoothChaos extends React.Component {
  state = { frame: 0 };

  componentDidMount() {
    const animate = () => {
      this.setState({ frame: this.state.frame + 1 });
      this.raf = requestAnimationFrame(animate);
    };
    animate();
  }

  componentWillUnmount() {
    cancelAnimationFrame(this.raf);
  }

  render() {
    return (
      <div>
        {Array(100).fill().map((_, i) => (
          <div 
            key={i}
            style={{
              position: 'absolute',
              left: `${50 + Math.sin((this.state.frame + i)/10) * 40}%`,
              top: `${50 + Math.cos((this.state.frame + i)/10) * 40}%`,
              width: '10px',
              height: '10px',
              background: 'red'
            }}
          />
        ))}
      </div>
    );
  }
}

"Help" from Web Workers

Even Web Workers can be used to create chaos:

// worker.js
self.onmessage = function(e) {
  setInterval(() => {
    self.postMessage(Math.random());
  }, 10);
};

// React component
class WorkerChaos extends React.Component {
  state = { value: 0 };
  worker = new Worker('worker.js');

  componentDidMount() {
    this.worker.onmessage = (e) => {
      this.setState({ value: e.data });
    };
  }

  componentWillUnmount() {
    this.worker.terminate();
  }

  render() {
    return <div>Worker data: {this.state.value}</div>;
  }
}

"Best Practices" with Third-Party Libraries

How to "correctly" use popular animation libraries to create chaos:

import { motion, AnimatePresence } from 'framer-motion';

const ListChaos = () => {
  const [items, setItems] = useState(
    Array(50).fill().map((_, i) => ({ id: i, value: Math.random() }))
  );

  useEffect(() => {
    const interval = setInterval(() => {
      setItems(prev => {
        const newItems = [...prev];
        // Randomly move, add, or remove items
        if (Math.random() > 0.7) newItems.pop();
        if (Math.random() > 0.7) newItems.unshift({ 
          id: Date.now(), 
          value: Math.random() 
        });
        if (Math.random() > 0.5) {
          const [removed] = newItems.splice(
            Math.floor(Math.random() * newItems.length), 1
          );
          newItems.splice(
            Math.floor(Math.random() * newItems.length), 0, removed
          );
        }
        return newItems;
      });
    }, 100);

    return () => clearInterval(interval);
  }, []);

  return (
    <AnimatePresence>
      {items.map((item, index) => (
        <motion.div
          key={item.id}
          layout
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.5 }}
          style={{
            padding: '10px',
            margin: '5px',
            background: `hsl(${item.value * 360}, 100%, 50%)`
          }}
        >
          {index}: {item.value.toFixed(2)}
        </motion.div>
      ))}
    </AnimatePresence>
  );
};

Form Handling Nightmare

Create the most user-unfriendly form experience:

class BadForm extends React.Component {
  state = {
    username: '',
    password: '',
    email: '',
    errors: {}
  };

  validate = () => {
    const errors = {};
    if (!this.state.username) errors.username = 'Required';
    if (this.state.username.length < 3) errors.username = 'Too short';
    if (this.state.username.length > 10) errors.username = 'Too long';
    if (!/\d/.test(this.state.password)) errors.password = 'Need a number';
    if (!/[A-Z]/.test(this.state.password)) errors.password = 'Need uppercase';
    if (!this.state.email.includes('@')) errors.email = 'Invalid email';
    
    // Trigger state update on every validation
    this.setState({ errors }, () => {
      // Validate again after validation
      setTimeout(this.validate, 100);
    });
  };

  handleChange = (e) => {
    const { name, value } = e.target;
    this.setState({ [name]: value }, this.validate);
  };

  render() {
    return (
      <form>
        <div>
          <label>Username:</label>
          <input 
            name="username" 
            value={this.state.username} 
            onChange={this.handleChange} 
          />
          {this.state.errors.username && 
            <span style={{ color: 'red' }}>{this.state.errors.username}</span>}
        </div>
        
        {/* Other form fields... */}
        
        <button type="submit">Submit</button>
      </form>
    );
  }
}

Global Event Listeners

Add numerous global event listeners in components:

class GlobalListener extends React.Component {
  state = { 
    mouseX: 0, 
    mouseY: 0,
    scrollY: 0,
    resizeCount: 0,
    keyPress: ''
  };

  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove);
    window.addEventListener('scroll', this.handleScroll);
    window.addEventListener('resize', this.handleResize);
    window.addEventListener('keydown', this.handleKeyDown);
  }

  componentWillUnmount() {
    // Intentionally not implemented, let listeners persist
  }

  handleMouseMove = (e) => {
    this.setState({ 
      mouseX: e.clientX,
      mouseY: e.clientY
    });
  };

  handleScroll = () => {
    this.setState({ scrollY: window.scrollY });
  };

  handleResize = () => {
    this.setState(prev => ({ resizeCount: prev.resizeCount + 1 }));
  };

  handleKeyDown = (e) => {
    this.setState({ keyPress: e.key });
  };

  render() {
    return (
      <div>
        <p>Mouse position: {this.state.mouseX}, {this.state.mouseY}</p>
        <p>Scroll position: {this.state.scrollY}</p>
        <p>Window resize count: {this.state.resizeCount}</p>
        <p>Last key pressed: {this.state.keyPress}</p>
      </div>
    );
  }
}

Recursive Components

Create infinitely recursive component structures:

const RecursiveTree = ({ depth = 0 }) => {
  const [expanded, setExpanded] = useState(false);
  const [childrenCount, setChildrenCount] = useState(3);

  useEffect(() => {
    // Randomly change child count
    const interval = setInterval(() => {
      setChildrenCount(Math.floor(Math.random() * 5) + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return (
    <div style={{ marginLeft: `${depth * 20}px` }}>
      <button onClick={() => setExpanded(!expanded)}>
        {expanded ? 'Collapse' : 'Expand'} Node {depth}
      </button>
      {expanded && (
        <div>
          {Array(childrenCount).fill().map((_, i) => (
            <RecursiveTree key={i} depth={depth + 1} />
          ))}
        </div>
      )}
    </div>
  );
};

// Usage
const App = () => <RecursiveTree />;

Dynamic Style Injection

Frequently manipulate styles via JavaScript:

class StyleChaos extends React.Component {
  state = { styles: {} };

  componentDidMount() {
    setInterval(() => {
      const newStyles = {};
      for (let i = 0; i < 100; i++) {
        newStyles[`prop${i}`] = Math.random() * 100;
      }
      this.setState({ styles: newStyles });
    }, 50);
  }

  render() {
    const style = {
      width: `${this.state.styles.prop1 || 100}px`,
      height: `${this.state.styles.prop2 || 100}px`,
      transform: `
        rotate(${this.state.styles.prop3 || 0}deg)
        scale(${1 + (this.state.styles.prop4 || 0) / 100})
      `,
      backgroundColor: `rgb(
        ${this.state.styles.prop5 || 255},
        ${this.state.styles.prop6 || 0},
        ${this.state.styles.prop7 || 0}
      )`,
      // More dynamic styles...
    };

    return <div style={style}>Dynamic Styles</div>;
  }
}

Unpredictable Component Keys

Randomly change component keys to force re-re

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

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.