React基础1

Thursday, April 15, 2021

JSX

const element = <h1>Hello, world!</h1>;
const component = <Component /> 

这种类似HTML又与JS相结合的代码称之为JSX,实际上JSX会倍babel转换成React.createElement,在babel官网可以得出转换结果。

image-20210617174609073React.createElement函数的返回值对象被我们称作虚拟DOM

使用

JSX 中可以嵌入表达式

const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

JSX 本身也是一个表达式

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

function getWorld (world) {
    if (world) {
        return <h1>Hello,World!</h1>;
    } else {
        return <h1>GG!{formatName(user)}!</h1>;
    }
}

JSX 特定属性

const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;

组件

React中分别有函数组件与 class 组件,函数组件没有内部状态和生命周期,但目前可以通过Hook实现类似的效果。

React 非常灵活,但它也有一个严格的规则:所有 React 组件都必须像纯函数一样保护它们的 props 不被更改

纯函数:不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。

// 函数组件
const Welcome = (props) => <h1>Hello, {props.name}</h1>

// class组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

以下以class组件为主

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

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

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

状态

类组件中通过this.setState修改数据,直接修改state,组件并不会重新触发render()。

// 错误
this.state.comment = 'Hello';

// 正确
this.setState({comment: 'Hello'});

State 的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

因为 this.propsthis.state 可能会异步更新,所以不要依赖他们的值来更新下一个状态。

this.setState({
    count: 1 // 假设原始值为0
})
console.log(this.state.count) // 输出0而不是1

想要在更新操作完成后执行操作,可传入回调函数。

this.setState({ count: 1 }, () => {
  console.log(this.state.count) // 输出1
})

如果依赖于之前的State,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数。

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment
});

// Correct
this.setState((state, props) => ({
  counter: state.count + props.increment
}))

事实上,在合成事件组件的生命周期setState是异步的;而在原生事件定时器setState是同步的。

这是因为,React内部维护了一个标识:isBatchingUpdates。在合成事件组件的生命周期中,该值为true,那么setState会被缓存进队列,最后才批量更新;而在原生事件定时器中,该值为false,调用setState时会直接同步更新。

生命周期

image-20210618112337591

事件

React中的event合成事件,不需要担心跨浏览器的兼容性问题。

// 方法一 避免this丢失
constructor() {
    this.handlerClick = this.handlerClick.bind(this)
}

// 方法二 使用箭头函数,初始化时已确认this,因每次渲染均会创建新函数,可能会有性能问题
<button onClick={(e) => this.handleClick(e)}>
    Click me
</button>

// 方法三 public class fields (公有实例字段可以在基类的构造过程中(构造函数主体运行前)使用Object.defineProperty添加)
class Btn extends React.component {
    handlerClick = (e) => {
        console.log(this)
    }

    render() {
        return (
        	 <button onClick={this.handlerClick}>
                Click me
             </button>
        )
    }
}

列表 & Key

key 帮助 React 识别哪些元素改变了,比如被添加或删除。当元素没有确定 id 的时候,万不得已可以使用元素索引 index 作为 key。

数组元素 中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值:

深度解析使用索引作为 key 的负面影响 深入解析为什么 key 是必须的

function NumberList(props) {
  const numbers = props.numbers
  return (
    <ul>
      {numbers.map((number) => (
        <ListItem key={number.toString()} value={number} />
      ))}
    </ul>
  )
}

表单

React表单元素分为受控组件非受控组件

使 React 的 state 成为“唯一数据源”,渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

// ...
constructor(props) {
    super(props)
    this.state = { value: '' }
  }

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

// ...
render() {
    return <input type="text" value={this.state.value} onChange={this.handleChange}/>
}

在 HTML 中,表单元素(如<input><textarea><select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新,这种称之为“非受控组件”

没有设置value或者设置为null的是一个非受控组件,对于非受控的input组件,用户的输入会直接反映在页面上,如果想要给组件设置一个非空的初始值,可以使用defaultValue属性。

render() {
    return <input type="text" value={null}/>
}

状态提升

多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。

在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。

示例