react17版本以前类组件常用操作

React 17 及以前版本(主要是 16.x 到 17.x)是类组件(Class Component)为主流的时代,函数组件(Function Component)当时还没有 Hooks(Hooks 是 React 16.8 引入的,但类组件仍是主流写法)。下面按常用功能逐一介绍。


一、组件传参(Props)

父组件向子组件传递数据,通过属性(props)实现。

bash 复制代码
// 父组件
class Parent extends React.Component {
  render() {
    return <Child name="张三" age={20} />;
  }
}

// 子组件
class Child extends React.Component {
  render() {
    return <div>{this.props.name},今年 {this.props.age} 岁</div>;
  }
}

特点:

  • props 是只读的,子组件不能修改
  • 可以传递任意类型:字符串、数字、对象、函数、JSX 节点
  • 通过 defaultProps 设置默认值:Child.defaultProps = { age: 18 }

二、数据绑定(State)

类组件通过 this.state 管理内部数据,通过 this.setState() 更新。

bash 复制代码
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    // setState 是异步的,更新会合并
    this.setState({ count: this.state.count + 1 });
    
    // 如果需要基于前一个状态更新,用函数形式
    this.setState(prevState => ({ count: prevState.count + 1 }));
  };

  render() {
    return (
      <div>
        <p>计数:{this.state.count}</p>
        <button onClick={this.increment}>+1</button>
      </div>
    );
  }
}

单向数据流: React 是单向绑定,数据从 state 流向视图,视图变化通过事件回调修改 state。


三、事件处理

bash 复制代码
class Form extends React.Component {
  handleClick = (e) => {
    e.preventDefault(); // 阻止默认行为
    console.log('点击了');
  };

  render() {
    return <button onClick={this.handleClick}>点击</button>;
  }
}

注意点:

  • 事件名用驼峰:onClick、onChange、onSubmit
  • 需要绑定 this(上面用了箭头函数,或者在 constructor 中 this.handleClick = this.handleClick.bind(this))

四、表单与双向绑定(受控组件)

React 中没有真正的 v-model,需要手动实现"双向绑定":

bash 复制代码
class InputForm extends React.Component {
  state = { value: '' };

  handleChange = (e) => {
    this.setState({ value: e.target.value });
  };

  handleSubmit = (e) => {
    e.preventDefault();
    alert('提交:' + this.state.value);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input 
          type="text" 
          value={this.state.value} 
          onChange={this.handleChange} 
        />
        <button type="submit">提交</button>
      </form>
    );
  }
}

非受控组件(用 ref 直接操作 DOM):

bash 复制代码
class Uncontrolled extends React.Component {
  inputRef = React.createRef();

  handleSubmit = () => {
    console.log(this.inputRef.current.value);
  };

  render() {
    return <input ref={this.inputRef} />;
  }
}

五、条件渲染 & 列表渲染

bash 复制代码
class ListDemo extends React.Component {
  state = {
    isLogin: true,
    list: ['苹果', '香蕉', '橙子']
  };

  render() {
    const { isLogin, list } = this.state;

    return (
      <div>
        {/* 条件渲染 */}
        {isLogin ? <p>欢迎回来</p> : <p>请登录</p>}
        
        {/* 也可以用 && */}
        {isLogin && <p>已登录</p>}

        {/* 列表渲染,必须加 key */}
        <ul>
          {list.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      </div>
    );
  }
}

key 的重要性: 帮助 React 识别哪些元素改变了,提升 diff 效率,列表中必须提供唯一 key。


六、生命周期(Class 组件核心)

React 16.3 之后生命周期分为三个阶段:

阶段 方法 说明
挂载 constructor 初始化 state 和绑定方法
static getDerivedStateFromProps 根据 props 更新 state(很少用)
render 返回 JSX
componentDidMount 组件挂载完成,适合发请求、操作 DOM
更新 static getDerivedStateFromProps 同上
shouldComponentUpdate 返回 false 可阻止渲染(性能优化)
render 重新渲染
getSnapshotBeforeUpdate 获取更新前 DOM 状态(很少用)
componentDidUpdate 更新完成
卸载 componentWillUnmount 清理工作:取消定时器、取消订阅

旧版(16.3 之前)还有:

  • componentWillMount、componentWillReceiveProps、componentWillUpdate(现已标记为不安全,不建议使用)

七、路由(React Router v4/v5)

React 17 时代主流是 React Router v5。

bash 复制代码
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';

// App.js
class App extends React.Component {
  render() {
    return (
      <Router>
        <nav>
          <Link to="/">首页</Link>
          <Link to="/about">关于</Link>
        </nav>
        
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/user/:id" component={User} />
          <Route component={NotFound} /> {/* 404 */}
        </Switch>
      </Router>
    );
  }
}

// 路由传参方式
class User extends React.Component {
  render() {
    // URL 参数:/user/123
    const { id } = this.props.match.params;
    
    // Query 参数:/user?id=123
    const query = new URLSearchParams(this.props.location.search);
    const id2 = query.get('id');
    
    return <div>用户ID:{id}</div>;
  }
}

常用 API:

  • <Link>:声明式导航
  • this.props.history.push('/path'):编程式导航
  • withRouter:让非路由组件也能获取 history、location、match

八、父子组件通信

1. 父 → 子:Props

bash 复制代码
<Child data={this.state.data} />

2. 子 → 父:回调函数

bash 复制代码
// 父
class Parent extends React.Component {
  getData = (data) => {
    console.log('子组件传来的:', data);
  };

  render() {
    return <Child onSend={this.getData} />;
  }
}

// 子
class Child extends React.Component {
  send = () => {
    this.props.onSend('子组件数据');
  };

  render() {
    return <button onClick={this.send}>传数据给父组件</button>;
  }
}

3. 兄弟组件通信:通过共同的父组件提升状态(State Lifting)

bash 复制代码
class Parent extends React.Component {
  state = { message: '' };

  setMessage = (msg) => {
    this.setState({ message: msg });
  };

  render() {
    return (
      <div>
        <SiblingA onSend={this.setMessage} />
        <SiblingB message={this.state.message} />
      </div>
    );
  }
}

九、跨层级通信(Context)

避免层层传递 props("prop drilling")。

bash 复制代码
// 1. 创建 Context
const ThemeContext = React.createContext('light');

// 2. 提供数据(Provider)
class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 3. 消费数据(Consumer)
class ThemedButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {theme => <button className={theme}>主题按钮</button>}
      </ThemeContext.Consumer>
    );
  }
}

Class 组件也可以用 static contextType:

bash 复制代码
class MyComponent extends React.Component {
  static contextType = ThemeContext;
  
  render() {
    return <div>{this.context}</div>;
  }
}

十、Refs(操作 DOM / 获取子组件实例)

bash 复制代码
class AutoFocus extends React.Component {
  inputRef = React.createRef();

  componentDidMount() {
    this.inputRef.current.focus();
  }

  render() {
    return <input ref={this.inputRef} />;
  }
}

回调 ref(旧写法):

bash 复制代码
<input ref={el => this.inputEl = el} />

十一、性能优化

方法 说明
shouldComponentUpdate 手动控制是否重新渲染
React.PureComponent 自动浅比较 props 和 state
React.memo(函数组件) 类似 PureComponent
拆分组件 避免不必要的渲染范围
bash 复制代码
class ListItem extends React.PureComponent {
  // 自动实现 shouldComponentUpdate 的浅比较
  render() {
    return <li>{this.props.name}</li>;
  }
}

总结对比(React 17 vs 18+)

特性 React 17 及以前 React 18+
主流写法 Class 组件 Function 组件 + Hooks
状态管理 this.state / this.setState useState / useReducer
副作用 componentDidMount 等生命周期 useEffect
上下文 static contextType / <Context.Consumer> useContext
路由 React Router v5 React Router v6(API 变化大)
并发特性 无 有(Concurrent Features)

如果你是在维护一个 React 17 的老项目,以上知识基本够用;如果是新项目,建议直接使用 React 18/19 + Hooks 写法。

相关推荐
程序员Sunday6 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei6 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
zhangzeyuaaa7 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙7 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯7 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪668 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
一个风轻云淡8 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u0111026759 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说10 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
libokaifa10 小时前
把语音模型塞进手机:sherpa-onnx 在 Android 上的 ASR / TTS
前端