文章目录
什么是React
React
是一个声明式,高效且灵活的用于构建用户界面的JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作组件。
搭建本地开发环境
- 安装Node.js
- 创建一个新项目
npx create-react-app sample-app
更新信息参考创建新的 React 应用
JSX
JSX
是一个 JavaScript 的语法扩展,可以生成 React “元素”。其格式比较像是模版语言,但事实上完全是在JavaScript内部实现的。(元素是构成React应用的最小单位,JSX就是用来声明React当中的元素,React使用JSX来描述用户界面)
JSX的本质不是模板语言,而是动态创建组件的一种语法糖,让你在JavaScript代码中直接编写HTML
以下两种代码是等效的
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
约定
- 小写的tag是原生的DOM节点
- 大写字母开头的为自定义组件
- JSX标记可以直接使用属性语法
在JSX中使用表达式
在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式。
- JSX本身也是表达式
const element = <h1>Hello, world!</h1>;
- 在属性中使用表达式
<MyComponent> foo={1+2+3}</MyComponent>
- 延展属性
const user = {
firstName: "淼",
lastName: "汪"
};
const greeting = <Greeting {...props} />;
- 表达式作为子元素
const element = <h1>{user.name}</h1>;
元素渲染
元素
是React应用程序的最小构件。 与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。
function tick() {
const element = (
<div>
<h1>Hello,World</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById("root"));
}
// 在 setInterval() 回调函数,每秒都调用 ReactDOM.render()。
setInterval(() => {
tick();
}, 1000);
组件和Props
组件允许你将UI分割成独立的、可重用的部分,并独立地考虑每个部分。
函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
渲染组件
使用ReactDOM.render()
渲染组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
生命周期和State
生命周期
来自:http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
示例
先上一个时钟的代码片段
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>
<h2>现在是 {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(<Clock></Clock>, document.getElementById("root"));
解释:
先说下State
,State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。直接使用this.state.comment = ‘Hello’;是无法修改state的(除非是在构造函数中,构造函数时唯一可以给this.state赋值的地方),但是你可以在通过this.setState()
来更新state。
componentDidMount()
方法(挂载)在组件已经被渲染到DOM中后运行,所以我们在此处设置计时器。
在componentWillUnmount()
方法(卸载)中,我们执行清除计时器操作。
数据是向下流动的
不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。
这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中:
事件处理
- React 事件的命名采用小驼峰式(camelCase),而不是DOM元素中的纯小写。
- 使用 JSX 语法时需要传入一个函数作为事件处理函数,而不是一个字符串。
在React中,事件处理方式如下:
<button onClick={activateLasers}>
Activate Lasers
</button>
在React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault
,若要阻止默认行为,参照如下:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
示例
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
// 为了在回调中使用`this`,这个绑定时必须的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? "开" : "关"}
</button>
);
}
}
ReactDOM.render(<Toggle></Toggle>, document.getElementById("root"));
列表和Key
基础列表组件
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
在运行以上代码后,有一个警告信息:
Each child in a list should have a unique ‘key’
我们可以通过<li key={number.toString()}>
设置key
Key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值。
元素的 key 只有放在就近的数组上下文中才有意义。我们修改下这个列表组件
function ListItem(props) {
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
表单
textarea 标签
class EasyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "在此处编写文章..."
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert("提交的文章:" + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
内容:
<textarea
value={this.state.value}
onChange={this.handleChange}
></textarea>
</label>
<input type="submit" value="提交"></input>
</form>
);
}
}
select 标签
class FruitForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: "strawberry" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert("你最喜欢的水果是:" + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你最喜欢的水果:
<select value={this.state.value} onChange={this.handleChange}>
<option value="apple">苹果</option>
<option value="watermelon">西瓜</option>
<option value="strawberry">草莓</option>
</select>
</label>
<input type="submit" value="提交"></input>
</form>
);
}
}
处理多个输入
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.name === "isGoing" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参与:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange}
></input>
</label>
<br />
<label>
来宾人数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange}
></input>
</label>
</form>
);
}
}
状态提升
状态提示是之我们需要多个组件需要反映相同的变化数据时,将共享状态提升到最近的公共父组件中。
我们在以下示例中描述了在指定温度(华氏度和摄氏度)下,水是否沸腾的案例。其中当华氏度(或摄氏度)中的一方值发生变化时,另一方应当自动计算。
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>水沸腾了</p>;
} else {
return <p>水还没有沸腾</p>;
}
}
const scaleNames = {
c: "摄氏度",
f: "华氏度"
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
// this.state = { temperature: "" };
}
handleChange(e) {
// this.setState({ temperature: e.target.value });
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>输入温度({scaleNames[scale]})</legend>
<input value={temperature} onChange={this.handleChange}></input>
</fieldset>
);
}
}
function toCelsius(fahrenheit) {
return ((fahrenheit - 32) * 5) / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9) / 5 + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return "";
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = { temperature: "", scale: "c" };
}
handleCelsiusChange(temperature) {
this.setState({ scale: "c", temperature });
}
handleFahrenheitChange(temperature) {
this.setState({ scale: "f", temperature });
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius =
scale === "f" ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit =
scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange}
></TemperatureInput>
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange}
></TemperatureInput>
<BoilingVerdict celsius={parseFloat(temperature)}></BoilingVerdict>
</div>
);
}
}
ReactDOM.render(<Calculator></Calculator>, document.getElementById("root"));