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 写法。

相关推荐
小码哥_常1 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
蜗牛攻城狮3 小时前
微前端四大方案选型对比与最佳实践:qiankun、micro-app、wujie、Module Federation
前端·微前端·qiankun·前端架构·micro-app·wujie
她说可以呀4 小时前
Vue插槽
前端·javascript·vue.js
qq_448011165 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
aixingpan5 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
2601_958352905 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
志尊宝6 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
FRing037 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
excel7 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
外收内放7 小时前
Python基础语法练习题(73)
java·前端·python