核心概念
JSX
JSX
内大括号可以写任意 js 代码,同理可以调用变量
ReactDOM.render
将指定 JSX 渲染到指定 DOM 上面去
const name = "Josh Perez";
const element = <h1>Hello, {
name}</h1>;
ReactDOM.render(element, document.getElementById("root"));
JSX 可以当做参数传入,或者返回
// 变量直接赋值JSX完全没问题
const element = <div tabIndex="0"></div>;
// user这个形参可以直接传入JSX
function getGreeting(user) {
return <h1>Hello, Stranger.</h1>;
}
两种创建 JSX 的方法
// 方法一
const element = <h1 className="greeting">Hello, world!</h1>;
// 方法二
const element = React.createElement(
"h1",
{
className: "greeting" },
"Hello, world!"
);
组件与传参
react 的组件定义最方便的方法是:直接用函数,接受一个形参即 props
自定义组件的属性直接传给形参 props,如下代码传参后的结果是: {name:'Sara'}
组件名必须大写开头
// 顶一个组件,props为参数
function Welcome(props) {
return <h1>Hello, {
props.name}</h1>;
}
// 针对自定义组件,所有的属性都统一作为参数传递给props
const element = <Welcome name="Sara" />;
ReactDOM.render(element, document.getElementById("root"));
把组件单独抽离出来便于复用,这是一个好习惯!
生命周期
若要使用 state 控制组件生命周期,请改用 class 来声明组件而非函数式
在同一 DOM 内多次调用 render 时,class 式声明的组件在其生命周期内只会存在一个实例!
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {
this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
this.props
与 this.state
是 react 内部字段
这是一个完整实现每隔 1s 更新一次时间的组件 Clock
- componentDidMount 挂载完毕钩子
- componentWillUnmount 卸载前钩子
class Clock extends React.Component {
// 组件的构造函数
constructor(props) {
super(props);
// state是内部值,直接向里面丢东西
this.state = {
date: new Date() };
}
// 挂载完毕,开启间隔计时器,每1s执行一次方法tick()
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000);
}
// 卸载前清除计时器
componentWillUnmount() {
clearInterval(this.timerID);
}
// 间隔更新函数,setState(差不多和微信小程序一个意思)修改值
tick() {
this.setState({
date: new Date(),
});
}
// render里面渲染DOM
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {
this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
// 渲染,没啥好说的
ReactDOM.render(<Clock />, document.getElementById("root"));
state 注意事项
- 更新 state 中的值务必使用
setState
方法,否则改数据后不会重新渲染! - state 更新可能是异步的,可以在 setState 方法内使用函数来替换对象写法
- state 是单向数据流,即父组件的 state 可以传递给其子孙组件并影响他们的渲染,但父组件的 state 无法影响其上级(如果父组件还存在一个父组件的话!)
事件处理
在 react 中,阻止组件的默认行为只能通过 js 代码实现
下面将一个函数抽离,并使用 preventDefault 阻止组件默认行为
function ActionLink() {
function handleClick(e) {
// 阻止组件的默认行为
e.preventDefault();
console.log("The link was clicked.");
}
return (
<a href="#" onClick={
handleClick}>
Click me
</a>
);
}
class 式创建自定义组件时,其中的方法不会自动绑定 this,所以需要我们手动改变其 this 指向到对应的 class 里面去
原理:当你调用一个方法时若不在结尾添加一个小括号,就必须明确其 this 指向,如下代码,我们调用 handleClick
方法时没有加小括号,故需要在 constructor
函数内改变其 this 指向
class Toggle extends React.Component {
constructor(props) {
...
// 改变class内的方法handleClick的this指向为本class
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
...
}
render() {
return (
// 此情况下必须要为方法handleClick指定this指向
<button onClick={
this.handleClick}>
{
this.state.isToggleOn ? "ON" : "OFF"}
</button>
);
}
}
这是两种等价的,为方法确定正确的 this 指向的写法
<button onClick={
(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={
this.deleteRow.bind(this, id)}>Delete Row</button>
条件渲染
与运算符&&
react 基本条件运算,根据之前我们所学知识,花括号内可以填写任意表达式;故以下代码判断,当 unreadMessages.length > 0
条件成立时就会执行 &&
运算符后面的 JSX
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{
unreadMessages.length > 0 && (
<h2>You have {
unreadMessages.length} unread messages.</h2>
)}
</div>
);
}
和 vue 一致,你也可以使用三元运算符
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{
isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
如果你想让组件不渲染,直接返回 null
return null
列表渲染
react 的列表渲染比较朴实一点,直接调用元素 map 迭代渲染
但和 vue 一样,最好使用 key,否则会出现警告
function NumberList(props) {
const numbers = props.numbers;
// 使用map取出numbers内部的所有值,渲染到指定JSX
const listItems = numbers.map((number) => (
// 万不得已的情况下可以直接拿值作为key
<li key={
number.toString()}>{
number}</li>
));
return <ul>{
listItems}</ul>;
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={
numbers} />,
document.getElementById("root")
);
不过官方推荐数据若没有给出 id 值的话,可以拿 map 的索引值作为 key
// 这里的todo为值,而index是map函数为我们生成的对应索引
const todoItems = todos.map((todo, index) => <li key={
index}>{
todo.text}</li>);
关于 key 使用的注意事项
- key 必须在 map 函数内定义!
- key 在其兄弟节点之间必须唯一,但是全局范围内可以重复
直接内联 return 一个列表渲染的 JSX,省去了冗余的中间变量
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{
numbers.map((number) => (
<ListItem key={
number.toString()} value={
number} />
))}
</ul>
);
}
受控组件
react 中的表单采用将所有数值都统一由 state 进行管理
代码解释:
表单 form 中的 input,其 value 属性直接由顶层 state 管理;onChange
也是由函数 handleChange
动态更新 state 中指定属性的值;
由于构造函数内 state 已经初始化为空字符串,故不用担心渲染 input 时会出错!
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "" };
// 没有小括号的函数调用必须要明确指定this指向
// 这里没有使用箭头函数的方法,而是使用bind绑定
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);
// 阻止默认的表单post或者get操作,由该方法进行自定义指定
event.preventDefault();
}
render() {
return (
<form onSubmit={
this.handleSubmit}>
<label>
名字:
<input
type="text"
value={
this.state.value}
onChange={
this.handleChange}
/>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
对于文本域 textarea
,我们不再向其内部写标签,直接使用 value
作为该文本域的内容
<textarea value={
this.state.value} onChange={
this.handleChange} />
select 组件里面的 value 直接指代当前激活的是哪一个 option,他的值等于当前几乎跌 option 的 value 值
<select value={
this.state.value} onChange={
this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
当存在多个 input 时,为他们添加 name 属性,设置不同名字,并在 onchange 函数内部进行判断即可动态更新对应的 state
handleInputChange(event) {
...
// 获取当前input的name属性值
const name = target.name;
// 根据键值对的方法,赋予指定name的值为value
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参与:
<input
name="isGoing"
type="checkbox"
checked={
this.state.isGoing}
onChange={
this.handleInputChange} />
</label>
...
</form>
);
}