惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
Jina AI
Jina AI
Stack Overflow Blog
Stack Overflow Blog
量子位
博客园_首页
Vercel News
Vercel News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Forbes - Security
Forbes - Security
IT之家
IT之家
N
News and Events Feed by Topic
S
Security Affairs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Webroot Blog
Webroot Blog
Recorded Future
Recorded Future
L
LangChain Blog
Y
Y Combinator Blog
AI
AI
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google Online Security Blog
Google Online Security Blog
V2EX - 技术
V2EX - 技术
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
PCI Perspectives
PCI Perspectives
I
Intezer
T
Tenable Blog
G
Google Developers Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Troy Hunt's Blog
L
LINUX DO - 最新话题
云风的 BLOG
云风的 BLOG
C
CXSECURITY Database RSS Feed - CXSecurity.com
有赞技术团队
有赞技术团队
O
OpenAI News
P
Proofpoint News Feed
TaoSecurity Blog
TaoSecurity Blog
C
Check Point Blog
Last Week in AI
Last Week in AI
S
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
Blog — PlanetScale
Blog — PlanetScale

博客园 - 沐子馨

什么是Agent? Agent Loop:从对话式 AI 到自主智能体的范式跃迁 解锁图数据建模的奥秘 基于 Neo4j 与 Milvus 的图RAG系统搭建指南 解决复杂推理难题:KG-RAG 在大模型中的应用 RAG 评估常用工具简介 RAG系统效果不好?一文看懂如何进行系统评估 写出让 AI “秒懂”的技能Skill 深入理解 RAG 中的格式化生成与函数调用 解锁 RAG 系统中的高级检索与重排序策略 掌握查询重构与智能路由的艺术 告别黑盒:手把手实现一个可解释、可调试的 Text2SQL 代理系统 告别简单向量搜索:RAG 中的高级查询构建与优化策略 深入理解 RAG 中的混合搜索策略 告别基础检索:掌握 RAG 中的句子窗口与递归路由策略 构建多模态检索系统:Milvus 部署、Schema 设计与混合检索实践 向量数据库原理与实战:从核心机制到 FAISS 应用 多模态向量嵌入:从文本到图像的语义统一与 RAG 应用 构建高效 RAG 的基石:深度解析 Embedding 模型原理与优化 All-in-RAG:解锁大模型“开卷考试”的终极能力 AI Agent 是如何思考的?一文读懂推理与决策引擎 终端革命:AI Agent 正在重新定义命令行 给企业装上“AI 大脑”:AgentSpace 如何从“手动检索”跨越到“智能决策”? Agentic 框架快速概览 AI 智能体交互如何带领它走出对话框,从屏幕像素迈向真实物理世界 高级提示词技巧如何带领大模型走出“一本正经胡说八道”的误区? 当 AI 学会“开疆拓土”:探索与发现模式如何从源头打破静态知识的桎梏,在陌生领域催生新洞见? 拒绝 AI “手忙脚乱”:优先级模式如何为智能体打造毫秒级的“任务调度官”? 构建生产级可信 Agent:评估与监控中的自动化审计与闭环优化机制 当 AI 学会“三思后言”:安全护栏如何从源头掐灭偏见、幻觉与恶意攻击? 拒绝“张口就来”:推理技术如何让 AI 像人类一样拆解复杂难题? 当家方知柴米贵:资源感知优化如何让 AI 智能体告别“算力浪费”? 开启多智能体互联时代:A2A 协议与 AI 协作网络的底层逻辑 为智能体装上“实时百科全书”:RAG 如何打破 AI 的知识边界? 构建负责任的AI:人类监督、干预与反馈闭环机制 当工具失效、网络中断:AI智能体的应急与自修复指南 赋予AI“北极星”:如何让智能体自主设定并追踪目标 打破“M×N”集成魔咒:MCP如何重塑AI应用架构 赋予模型“生命力”:大语言模型如何通过学习实现持续适应 赋予模型“长期记忆”:大语言模型的记忆管理机制 AI的“社会性”:深入理解多智能体协作机制 理解AI智能体的规划机制 理解大模型的工具使用 智能体的元认知:反思模式与自我优化机制 智能体并行化模式:提升复杂工作流性能的关键技术 超越线性执行:使用LLM实现智能体路由模式 解构复杂任务:深入理解提示词链(Prompt Chaining) 别再被 AI 术语唬住了!一文理清你需要了解的所有概念 不止是多写几句:谷歌如何用69页白皮书重塑提示工程 [转]长PDF文档的总结与评估策略 Context 工程:如何把正确的上下文喂给 AI 前端如何写出优秀的 AI Agent Skills mac安装python后command not found: pip keep-alive 原理剖析 如何优雅地在 React 中使用TypeScript,看这一篇就够了! react useContext 以用户为中心的性能指标【译】 element下拉框远程搜索debounce防抖控制 vue-router打开新窗口 propmise: allSettled()与all()的区别 使用 new Date() 在chrome、安卓和 IOS 中表现不同
React Context 详细介绍(状态共享、数据传递)
沐子馨 · 2023-01-12 · via 博客园 - 沐子馨

Context 是什么?
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

Context 什么时候用?
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。举个例子,在下面的代码中,我们通过一个 “theme” 属性手动调整一个按钮组件的样式。

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}
 
function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}
 
class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}
 
// 通过props传递:App -> Toolbar -> ThemedButton
// 如果嵌套很深,那么需要逐层传递props,即使中间不需要该props,显得很繁琐

使用 context, 我们可以避免通过中间元素传递 props。

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context("light"为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}
 
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}
 
class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
// 也可以使用 ThemedButto.contextType = ThemeContext;

API介绍

React.createContext

const MyContext = React.createContext(defaultValue);

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

Context.Provider

<MyContext.Provider value={/* 某个值 */}>

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。

Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。

当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

Class.contextType
挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中

import MyContext from './MyContext';
 
class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
  // 或者如上边例子一样使用 static contextType = MyContext;
}
MyClass.contextType = MyContext;

Context.Consumer

import MyContext from './MyContext';
 
function ToolList() {
  return (
    <MyContext.Consumer
      {value => /* 基于 context 值进行渲染*/}
    </MyContext.Consumer>
  )
}

这里,React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。

这需要函数作为子元素(function as a child)这种做法。这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。

Context.displayName
context 对象接受一个名为 displayName 的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。

如下述组件在 DevTools 中将显示为 MyDisplayName

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
 
<MyContext.Provider>   // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer>   // "MyDisplayName.Consumer" 在 DevTools 中

示例

动态 Context

对于上面的 theme 例子,使用动态值(dynamic values)后更复杂的用法

theme-context.js

export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};
 
export const ThemeContext = React.createContext(themes.dark);   // 该处为默认值

themed-button.js

import { ThemeContext } from './theme-context';
 
class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    // 获取到ThemeContext中的默认值
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
  // static contextType = ThemeContext;
}
ThemedButton.contextType = ThemeContext;
 
export default ThemedButton;

app.js

import { ThemeContext, themes } from './theme-context';
import ThemedButton from './themed-button';
 
// 一个使用 ThemedButton 的中间组件
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}
 
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };
 
    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };
  }
 
  render() {
    // 在 ThemeProvider 内部的 ThemedButton 按钮组件使用 state 中的 theme 值,
    // 而外部的组件使用默认的 theme 值
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}
 
ReactDOM.render(<App />, document.root);
 
// 使用ThemeContext.Provider包裹的组件,可以消费到ThemeContext中的value
// 即Toolbar、ThemedButton中都可以使用this.context来获取到value
// 注意观察,更新state的方法是通过props向下传递,由子孙组件触发更新,下面会讲到通过context的方式传递更新函数

在嵌套组件中更新 Context
在上面的例子中,我们通过 props 的方式向下传递一个更新函数,从而改变 App 中 themes 的值。我们知道,从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context

theme-context.js

// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},   // 定义更新主题的方法,向下传递
});

theme-toggler-button.js

import { ThemeContext } from './theme-context';
 
function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数(下面app.js部分)
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}
 
export default ThemeTogglerButton;

app.js

import { ThemeContext, themes } from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
 
class App extends React.Component {
  constructor(props) {
    super(props);
 
    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };
 
    // State 也包含了更新函数,因此它会被传递进 context provider。
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,   // 定义更新函数,通过context方式向下传递
    };
  }
 
  render() {
    // 整个 state 都被传递进 provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}
 
function Content() {
  return (
    <div>
      <ThemeTogglerButton />
    </div>
  );
}
 
ReactDOM.render(<App />, document.root);

消费多个 Context

为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点

// Theme context,默认的 theme 是 "light" 值
const ThemeContext = React.createContext('light');
 
// 用户登录 context
const UserContext = React.createContext({
  name: 'Guest',
});
 
class App extends React.Component {
  render() {
    const { signedInUser, theme } = this.props;
 
    // 提供初始 context 值的 App 组件
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}
 
function Layout() {
  return (
    <div>
      <Sidebar />
      <Content />
    </div>
  );
}
 
// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

如果两个或者更多的 context 值经常被一起使用,那你可能要考虑一下另外创建你自己的渲染组件,以提供这些值。

注意事项
因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

为了防止这种情况,将 value 状态提升到父节点的 state 里

class App extends React.Component {
  constructor(props) {
    super(props);
    // 多次渲染,state 会被保留,当value不变时,下面的 consumers 组件不会重新渲染 
    this.state = {
      value: {something: 'something'},
    };
  }
 
  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}

原文链接:https://blog.csdn.net/qq_41809113/article/details/122460679