React在状态、类和函数方面的工作方式已经发生了范式转变。React现在有一个叫做钩子的功能,它看起来是一个游戏规则的改变者。通过阅读react文档和观看React Conf,看起来我们这些网络开发者应该熟悉React中的钩子,因为它们可以使React代码更简洁。类组件仍然被支持,但看起来Hooks与功能组件的结合是未来最受欢迎的方法。
介绍useState()
useState()钩子是我们要学习的第一个钩子,为了使用它,我们把它导入到一个文件中,就像这样。
import React, { useState } from 'react';
useState()函数是一个钩子,可以让你在React中为函数组件添加状态。在这之前,只有类组件可以使用状态。
设置一个状态变量
我们可以使用这个钩子来分配一个状态变量。想象一下,我们要创建一个Todo应用程序,我们可以把useState()像这样工作。
import React, { useState } from "react";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
console.log(todos);
console.log(setTodos);
return <div />;
}
export default App;
当你调用useState()时,它会返回两样东西。当前的状态,和一个你可以用来更新该状态的 函数。在上面的代码中,一个包含三个对象的数组被分配给了 todos变量,而一个可以更新该状态的函数被存储在 setTodos.
useState()返回一个有状态的值,以及一个更新它的函数。

我们可以在控制台中看到注销的结果 **todos**和 setTodos.前者保存了三个对象的数组,后者保存了一个函数,我们可以通过f字符看到。
因此,让我们继续渲染我们存储在变量中的这三个对象。 todos变量中的三个对象。我们了解到,当用 react 创建一个列表时,我们可以像这样使用 map 函数。
import React, { useState } from "react";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
return (
<React.Fragment>
{todos.map((todo, index) => (
<div key={index} className="card mb-3">
<div className="card-body">
<h5 className="card-title">{todo.text}</h5>
</div>
</div>
))}
</React.Fragment>
);
}
export default App;
我们现在有三件事要做

Todo功能组件
让我们把渲染Todo项目的标记移到一个专门的组件中。在 src/components 中创建一个名为todo.jsx的新文件,并添加这段代码。
import React from "react";
function Todo({ todo, index }) {
return (
<React.Fragment>
<div key={index} className="card mb-3">
<div className="card-body">
<h5 className="card-title">{todo.text}</h5>
</div>
</div>
</React.Fragment>
);
}
export default Todo;
现在在主App.js文件中,进行突出的更新。
import React, { useState } from "react";
import Todo from "./components/todo";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
return (
<React.Fragment>
{todos.map((todo, index) => (
<Todo todo={todo} key={index} />
))}
</React.Fragment>
);
}
export default App;
现在我们仍然在页面上渲染了三个todo项目,但使用的是Function Components来完成。
添加一个文本输入
我们需要更多的事情要做。为此,我们可以创建一个新的组件,它将容纳一个表单和文本输入字段来添加一个新的todo。很快我们将看到如何更新状态添加一个名为todoform.jsx的新文件并将其添加到src/components中。
import React, { useState } from "react";
function TodoForm() {
const [value, setValue] = useState("");
return (
<form>
<input
type="text"
className="form-control"
value={value}
onChange={e => setValue(e.target.value)}
placeholder="Type then hit *Enter*"
/>
</form>
);
}
export default TodoForm;
现在我们可以在主App.js文件中导入并渲染它。
import React, { useState } from "react";
import Todo from "./components/todo";
import TodoForm from "./components/todoform";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
return (
<React.Fragment>
{todos.map((todo, index) => (
<Todo todo={todo} key={index} />
))}
<TodoForm />
</React.Fragment>
);
}
export default App;
调用setTodos()
我们已经准备好更新我们的 todos变量的状态。我们可以在组件中引发一个事件,然后在组件中处理该事件。我们可以在App.js中通过传递一个道具作为的属性,以及定义handleAddTodo()事件处理程序来开始。两者都在这里被强调。
import React, { useState } from "react";
import Todo from "./components/todo";
import TodoForm from "./components/todoform";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
const handleAddTodo = text => {
const newToDos = [...todos, { text }];
setTodos(newToDos);
};
return (
<React.Fragment>
{todos.map((todo, index) => (
<Todo todo={todo} key={index} />
))}
<TodoForm onAddTodo={handleAddTodo} />
</React.Fragment>
);
}
export default App;
在这一点上,表单有一个onSubmit动作。这意味着当用户点击 "Enter "键时,表单的值将被传递给 handleSubmit函数。在该函数中,一个 onAddTodo事件被引发。我们可以访问这个事件,因为它是作为一个道具被传递到组件中的。当这个事件被引发时, handleAddTodo函数在App.js中运行。在这一点上。 setTodos()被调用,并且状态被更新
将一个Todo标记为完成
现在让我们在组件中添加一个按钮,以便能够将一个项目标记为完成。要做到这一点,有几个步骤。首先,我们可以在todo.jsx组件文件中添加这个按钮。
import React from "react";
function Todo({ todo, index, onComplete }) {
return (
<React.Fragment>
<div key={index} className="card mb-3">
<div className="card-body">
<h5
className="card-title"
style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
>
{todo.text}
</h5>
<button onClick={() => onComplete(index)} className="btn btn-success">
Mark Complete
</button>
</div>
</div>
</React.Fragment>
);
}
export default Todo;
以上是重要的亮点。在第3行,我们需要接受 onComplete托词到组件中。在第10行,我们熟悉的三元操作符的使用,是为了决定是否对todo项目的文本进行穿行处理。最后,你可以看到一个新的按钮,当点击它时,会引发 onComplete事件,同时也接受索引作为一个参数。我们需要这个索引来知道哪个todo项目已经完成。现在我们需要对App.js进行以下更新。
import React, { useState } from "react";
import Todo from "./components/todo";
import TodoForm from "./components/todoform";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
const handleAddTodo = text => {
const newToDos = [...todos, { text }];
setTodos(newToDos);
};
const handleComplete = index => {
const newToDos = [...todos];
newToDos[index].isCompleted = true;
setTodos(newToDos);
};
return (
<React.Fragment>
{todos.map((todo, index) => (
<Todo
todo={todo}
key={index}
onComplete={handleComplete}
index={index}
/>
))}
<TodoForm onAddTodo={handleAddTodo} />
</React.Fragment>
);
}
export default App;
有一个新的事件处理程序,名为 handleComplete()的事件处理程序。这个事件处理程序在用户点击按钮时运行,并引发了 onComplete事件。再一次,我们看到setTodos()函数被调用在 handleComplete()的内部调用,以更新状态。最后,注意到嵌入的有两个新的属性,即 onComplete和 index.这就是我们可以在todo.jsx中访问它们的原因。这是React的一个有时很棘手的部分,那就是跟踪组件之间的数据共享。所以我们现在来看看这个动作。
将一个Todo标记为未完成
按照我们上面使用的惯例,现在应该很容易实现一个按钮,当点击该按钮时,将一个todo标记为未完成。
todo.jsx
import React from "react";
function Todo({ todo, index, onComplete, onUnfinished }) {
return (
<React.Fragment>
<div key={index} className="card mb-3">
<div className="card-body">
<h5
className="card-title"
style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
>
{todo.text}
</h5>
<button onClick={() => onComplete(index)} className="btn btn-success">
Mark Complete
</button>{" "}
<button
onClick={() => onUnfinished(index)}
className="btn btn-secondary"
>
Mark Unfinished
</button>
</div>
</div>
</React.Fragment>
);
}
export default Todo;
App.js
import React, { useState } from "react";
import Todo from "./components/todo";
import TodoForm from "./components/todoform";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
const handleAddTodo = text => {
const newToDos = [...todos, { text }];
setTodos(newToDos);
};
const handleComplete = index => {
const newToDos = [...todos];
newToDos[index].isCompleted = true;
setTodos(newToDos);
};
const handleUnfinished = index => {
const newToDos = [...todos];
newToDos[index].isCompleted = false;
setTodos(newToDos);
};
return (
<React.Fragment>
{todos.map((todo, index) => (
<Todo
todo={todo}
key={index}
onComplete={handleComplete}
index={index}
onUnfinished={handleUnfinished}
/>
))}
<TodoForm onAddTodo={handleAddTodo} />
</React.Fragment>
);
}
export default App;
现在我们也可以把一个todo标记为未完成。
删除一个Todo
为了完善这个应用程序,我们可以再添加一个按钮来删除一个todo。再一次,这遵循了同样的模式。
App.js
import React, { useState } from "react";
import Todo from "./components/todo";
import TodoForm from "./components/todoform";
function App() {
const [todos, setTodos] = useState([
{ text: "Learn React Hooks", isCompleted: false },
{ text: "Take a Drive", isCompleted: false },
{ text: "Play some games", isCompleted: false }
]);
const handleAddTodo = text => {
const newToDos = [...todos, { text }];
setTodos(newToDos);
};
const handleComplete = index => {
const newToDos = [...todos];
newToDos[index].isCompleted = true;
setTodos(newToDos);
};
const handleUnfinished = index => {
const newToDos = [...todos];
newToDos[index].isCompleted = false;
setTodos(newToDos);
};
const handleDeleteTodo = index => {
const newToDos = [...todos];
newToDos.splice(index, 1);
setTodos(newToDos);
};
return (
<React.Fragment>
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
onComplete={handleComplete}
onUnfinished={handleUnfinished}
onDeleteTodo={handleDeleteTodo}
/>
))}
<TodoForm onAddTodo={handleAddTodo} />
</React.Fragment>
);
}
export default App;
todo.jsx
import React from "react";
function Todo({ todo, index, onComplete, onUnfinished, onDeleteTodo }) {
return (
<React.Fragment>
<div className="card mb-3">
<div className="card-body">
<h5
className="card-title"
style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
>
{todo.text}
</h5>
<button onClick={() => onComplete(index)} className="btn btn-success">
Mark Complete
</button>{" "}
<button
onClick={() => onUnfinished(index)}
className="btn btn-secondary"
>
Mark Unfinished
</button>{" "}
<button
onClick={() => onDeleteTodo(index)}
className="btn btn-danger"
>
Delete Todo
</button>
</div>
</div>
</React.Fragment>
);
}
export default Todo;
最后的结果允许你添加一个todo,将一个todo标记为完成,将一个todo标记为未完成,以及删除一个todo。
React useState钩子总结
在本教程中,我们看了一下React中新的useState()钩子。在这样做的时候,我们建立了一个小的todo应用程序。useState()函数现在允许Web开发者为Function Components添加状态处理能力,而在过去,只有Class Components有这样的能力。此外,这种新的方法消除了在JavaScript中使用这种方法的混乱性。