1.先通过一个简单案例说一下二者的写法
import { useState , useContext, createContext } from 'react'
import './App.css'
const countContext = createContext(0);
function App() {
const [count, setCount] = useState(0)
return (
<div className="App">
<button onClick={() => setCount(count + 2)}>按钮值是:{count}</button>
<countContext.Provider value={count}>
<Son />
<Children />
</countContext.Provider>
</div>
)
}
const Son = () => {
const Num = useContext(countContext);
return <h2>第一个子接收值是:{Num}</h2>
}
const Children = () => {
return (
<countContext.Consumer>
{
n => <h2>第二个组件接收到的值是:{n}</h2>
}
</countContext.Consumer>
)
}
export default App
效果图:
2.使用react 官网标题的例子: Heading.jsx
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
export default function Heading({ children }) {
const level = useContext(LevelContext);
switch (level) {
case 0:
throw Error('Heading 必须在 Section 内部!');
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('未知的 level:' + level);
}
}
Section.jsx
import {useContext} from 'react';
import {LevelContext} from './LevelContext';
export default function Section ({children}) {
const level = useContext(LevelContext);
return (
<section>
<LevelContext.Provider value={level + 1}>
{children}
</LevelContext.Provider>
</section>
)
}
LevelContext.js
import {createContext} from 'react';
export const LevelContext = createContext(1);
新建Title.jsx
import Heading from './Heading';
import Section from './Section';
export default function Title() {
return (
<Section>
<Heading>主标题</Heading>
<Section>
<Heading>副标题</Heading>
<Heading>副标题</Heading>
<Heading>副标题</Heading>
<Section>
<Heading>子标题</Heading>
<Heading>子标题</Heading>
<Heading>子标题</Heading>
<Section>
<Heading>子子标题</Heading>
<Heading>子子标题</Heading>
<Heading>子子标题</Heading>
</Section>
</Section>
</Section>
</Section>
);
}
效果如下:
3.## Context 会穿过中间层级的组件 App.js
import Heading from './Heading';
import Section from './Section';
export default function Title() {
return (
<Section>
<Heading>My Profile</Heading>
<Post
title="旅行者,你好!"
body="来看看我的冒险。"
/>
<AllPosts />
</Section>
);
}
function AllPosts () {
return (
<Section>
<Heading>帖子</Heading>
<RecentPosts />
</Section>
)
}
function RecentPosts () {
return (
<Section>
<Heading>最近的帖子</Heading>
<Post
title="里斯本的味道"
body="...那些蛋挞!"
/>
<Post
title="探戈节奏中的布宜诺斯艾利斯"
body="我爱它!"
/>
</Section>
)
}
function Post ({title, body}) {
return (
<Section isFancy={true}>
<Heading>
{title}
</Heading>
<p><i>{body}</i></p>
</Section>
)
}
Heading.jsx
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
export default function Heading({ children }) {
const level = useContext(LevelContext);
switch (level) {
case 0:
throw Error('Heading 必须在 Section 内部!');
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('未知的 level:' + level);
}
}
Section.jsx
import {useContext} from 'react';
import {LevelContext} from './LevelContext';
export default function Section ({children, isFancy}) {
const level = useContext(LevelContext);
return (
<section className={
'section' +
(isFancy ? 'fancy' : '')
}>
<LevelContext.Provider value={level + 1}>
{children}
</LevelContext.Provider>
</section>
)
}
LevelContext.js
import {createContext} from 'react';
export const LevelContext = createContext(0);
4.## 结合使用 reducer 和 context TaskApp.jsx
import {useReducer} from 'react';
import AddTask from './AddTask.jsx';
import TaskList from './TaskList.jsx';
export default function TaskApp() {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
function handleAddTask(text){
dispatch({
type: 'added',
id: nextId++,
text: text,
})
};
function handleChangeTask(task){
dispatch({
type: 'changed',
task: task,
})
};
function handleDeleteTask(taskId){
dispatch({
type: 'deleted',
id: taskId,
})
};
return (
<>
<h1>Day off in Kyoto</h1>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
)
}
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks,{
id: action.id,
text: action.text,
done: false
}];
}
case 'changed': {
return tasks.map(t => {
if(t.id === action.task.id){
return action.task;
}else {
return t;
}
})
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default : {
throw Error('Unknown action: ' + action.type);
}
}
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: 'Philosopher’s Path', done: true },
{ id: 1, text: 'Visit the temple', done: false },
{ id: 2, text: 'Drink matcha', done: false }
];
AddTask.jsx
import {useState} from 'react'
export default function AddTask ({onAddTask}) {
const [text, setText] = useState('');
return (
<>
<input
placeholder='Add Task'
value={text}
onChange={e => setText(e.target.value)}
/>
<button
onClick={()=>{
setText('');
onAddTask(text);
}}
>
Add
</button>
</>
)
}
TaskList.jsx
import {useState} from 'react';
export default function TaskList ({tasks, onChangeTask, onDeleteTask}) {
return (
<ul>
{
tasks.map(task => (
<li key={task.id}>
<Task
task={task}
onChange={onChangeTask}
onDelete={onDeleteTask}
/>
</li>
))
}
</ul>
)
}
function Task ({task, onChange, onDelete}) {
const [isEditing, setIsEditing] = useState(false);
let taskContent;
if(isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={e=>{
onChange({
...task,
text: e.target.value
})
}}
/>
<button onClick={()=> setIsEditing(false)}> Save</button>
</>
)
} else {
taskContent = (
<>
{task.text}
<button onClick={() => setIsEditing(true) }>
Edit
</button>
</>
)
}
return (
<label>
<input
type='checkbox'
checked={task.done}
onChange={e=> {
onChange({
...task,
done: e.target.checked
})
}}
/>
{taskContent}
<button onClick={()=> onDelete(task.id)}>Delete</button>
</label>
)
}
效果如下:
5.使用 reducer 和 context修改上述例子
(1)### 将 state 和 dispatch 函数 放入 context 新建TasksContext.jsx
import {createContext} from 'react';
export const TasksContext = createContext(null);
export const TaskDispatchContext = createContext(null);
TaskApp.jsx
import {TasksContext, TaskDispatchContext} from './TasksContext';
<TasksContext.Provider value={tasks}>
<TaskDispatchContext.Provider value={dispatch}>
<h1>Day off in Kyoto</h1>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</TaskDispatchContext.Provider>
</TasksContext.Provider>
(2)### 在组件树中的任何地方使用 context
TaskApp.jsx
import {useState, useContext} from 'react';
import {TaskDispatchContext} from './TasksContext';
export default function AddTask () {
const [text, setText] = useState('');
const diapatch = useContext(TaskDispatchContext);
return (
<>
<input
placeholder='Add Task'
value={text}
onChange={e => setText(e.target.value)}
/>
<button
onClick={()=>{
setText('');
diapatch({
type: 'added',
id: nextId++,
text: text,
})
}}
>
Add
</button>
</>
)
}
let nextId = 3;
TasksContext.js
import {createContext} from 'react';
export const TasksContext = createContext(null);
export const TaskDispatchContext = createContext(null);
TaskList.jsx
import {useState, useContext} from 'react';
import {TasksContext, TaskDispatchContext} from './TasksContext';
export default function TaskList () {
const tasks = useContext(TasksContext);
return (
<ul>
{
tasks.map(task => (
<li key={task.id}>
<Task
task={task}
/>
</li>
))
}
</ul>
)
}
function Task ({task}) {
const [isEditing, setIsEditing] = useState(false);
const diapatch = useContext(TaskDispatchContext);
let taskContent;
if(isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={e=>{
diapatch({
type: 'changed',
task: {
...task,
text: e.target.value,
}
})
}}
/>
<button onClick={()=> setIsEditing(false)}> Save</button>
</>
)
} else {
taskContent = (
<>
{task.text}
<button onClick={() => setIsEditing(true) }>
Edit
</button>
</>
)
}
return (
<label>
<input
type='checkbox'
checked={task.done}
onChange={e=> {
diapatch({
type: 'changed',
task: {
...task,
done: e.target.checked
}
})
}}
/>
{taskContent}
<button onClick={()=> {
diapatch({
type: 'deleted',
id: task.id
})
}}>
Delete
</button>
</label>
)
}
TaskApp.jsx
import {useReducer} from 'react';
import AddTask from './AddTask.jsx';
import TaskList from './TaskList.jsx';
import {TasksContext, TaskDispatchContext} from './TasksContext';
export default function TaskApp() {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
return (
<>
<TasksContext.Provider value={tasks}>
<TaskDispatchContext.Provider value={dispatch}>
<h1>Day off in Kyoto</h1>
<AddTask />
<TaskList />
</TaskDispatchContext.Provider>
</TasksContext.Provider>
</>
)
}
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks,{
id: action.id,
text: action.text,
done: false
}];
}
case 'changed': {
return tasks.map(t => {
if(t.id === action.task.id){
return action.task;
}else {
return t;
}
})
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default : {
throw Error('Unknown action: ' + action.type);
}
}
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: 'Philosopher’s Path', done: true },
{ id: 1, text: 'Visit the temple', done: false },
{ id: 2, text: 'Drink matcha', done: false }
];
(3)## 将相关逻辑迁移到一个文件当中
TaskApp.jsx
import AddTask from './AddTask.jsx';
import TaskList from './TaskList.jsx';
import {TasksProvider} from './TasksContext';
export default function TaskApp() {
return (
<>
<TasksProvider>
<h1>Day off in Kyoto</h1>
<AddTask />
<TaskList />
</TasksProvider>
</>
)
}
TasksContext.jsx
import {createContext, useReducer, useContext} from 'react';
export const TasksContext = createContext(null);
export const TaskDispatchContext = createContext(null);
export function TasksProvider({ children }){
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
return (
<>
<TasksContext.Provider value={tasks}>
<TaskDispatchContext.Provider value={dispatch}>
{children}
</TaskDispatchContext.Provider>
</TasksContext.Provider>
</>
)
}
export function useTasks(){
return useContext(TasksContext);
}
export function useTasksDispatch(){
return useContext(TaskDispatchContext);
}
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks,{
id: action.id,
text: action.text,
done: false
}];
}
case 'changed': {
return tasks.map(t => {
if(t.id === action.task.id){
return action.task;
}else {
return t;
}
})
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default : {
throw Error('Unknown action: ' + action.type);
}
}
}
const initialTasks = [
{ id: 0, text: 'Philosopher’s Path', done: true },
{ id: 1, text: 'Visit the temple', done: false },
{ id: 2, text: 'Drink matcha', done: false }
];
AddTask.jsx
import {useState} from 'react';
import {useTasksDispatch} from './TasksContext';
export default function AddTask () {
const [text, setText] = useState('');
const diapatch = useTasksDispatch();
return (
<>
<input
placeholder='Add Task'
value={text}
onChange={e => setText(e.target.value)}
/>
<button
onClick={()=>{
setText('');
diapatch({
type: 'added',
id: nextId++,
text: text,
})
}}
>
Add
</button>
</>
)
}
let nextId = 3;
TaskList.jsx
import {useState, useContext} from 'react';
import {useTasks, useTasksDispatch} from './TasksContext';
export default function TaskList () {
const tasks = useTasks();
return (
<ul>
{
tasks.map(task => (
<li key={task.id}>
<Task
task={task}
/>
</li>
))
}
</ul>
)
}
function Task ({task}) {
const [isEditing, setIsEditing] = useState(false);
const diapatch = useTasksDispatch();
let taskContent;
if(isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={e=>{
diapatch({
type: 'changed',
task: {
...task,
text: e.target.value,
}
})
}}
/>
<button onClick={()=> setIsEditing(false)}> Save</button>
</>
)
} else {
taskContent = (
<>
{task.text}
<button onClick={() => setIsEditing(true) }>
Edit
</button>
</>
)
}
return (
<label>
<input
type='checkbox'
checked={task.done}
onChange={e=> {
diapatch({
type: 'changed',
task: {
...task,
done: e.target.checked
}
})
}}
/>
{taskContent}
<button onClick={()=> {
diapatch({
type: 'deleted',
id: task.id
})
}}>
Delete
</button>
</label>
)
}