We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文作者:IMWeb howenhuo 原文出处:IMWeb社区 未经同意,禁止转载
原文链接:How To Master Advanced React Design Patterns: Context API
使用高级设计模式创建灵活可重用的React组件 - 第1部分:复合组件
在本系列的上一部分中,我们探讨了如何使用复合组件和静态类方法来创建灵活可重用的组件。使用我们创造的API,我们能够以声明的方式来动态重建各种变化的组件
我们可以轻松添加任意数量的 step,我们可以决定 progress 是在左侧还是右侧。
step
progress
class App extends Component { render() { return ( <Stepper stage={1}> <Stepper.Progress> <Stepper.Stage num={1} /> </Stepper.Progress> <Stepper.Steps> <Stepper.Step num={1} text={"Stage 1"}/> </Stepper.Steps> </Stepper> ); } } export default App;
我们能够做到这一点是因为我们使用了一些 React API 辅助函数将所需的属性传递给组件树中的每个子项; stage 和 handleClick 属性可被需要它们的组件访问。
stage
handleClick
但是这种技术存在一个主要缺陷。 props 只能传递给他们的直接子项。 这使得 API 非常僵硬,它要求 Stepper.Steps 组件必须是 Stepper 组件的直接子组件,否则 props 传递会中断。 这在灵活性上存有巨大影响。
props
Stepper.Steps
Stepper
如果我们想要使用 flexbox 添加标题怎么办?
class App extends Component { render() { return ( <Stepper stage={1}> <Stepper.Progress> <Stepper.Stage num={1} /> </Stepper.Progress> <div style={{flex: 1, display: 'flex', flexDirection: 'column'}}> <Stepper.Header title="Stepper Heading"/> <Stepper.Steps> <Stepper.Step num={1} text={"Stage 1"}/> </Stepper.Steps> </div> </Stepper> ); } }
通过添加一个简单的 div ,我们完全破坏了组件。 Stepper.Steps 组件不再是 Stepper 组件的直接子组件,因此无法接收其 props。
那有没有一种灵活的,仅需要小调整就能达到我们预期的方法呢?
答案就是:Context !!
Context
React Context 已经存在了一段时间,但 React 工程师非常清楚它是实验性的,并且很可能在不久的将来会废弃。 好消息的是从 React 16.3 开始,它已经稳定了,我们可以在整个 React 应用程序中使用它。
React Context
那么我们一直听到的这个 Context 是什么?
我无法给出比 React 官方文档更清晰的定义:
Context 提供了一种在组件之间共享数据的方式,而不必通过组件树的每个层级显式地传递 props。
这正好解决我们的问题! 使用 Context,我们不再需要遍历并克隆每个子项来传递所需的 props。 Context 的设计让我们可以共享“全局”状态,并在 React 树中任何位置获取。
接着,让我展示给你如何使用和运行 Context API 的步骤。
Context API
React 现在带有一个名为 createContext 的方法。 我们需要做的只是调用此方法并将其赋给一个变量。
createContext
export const StepperContext = React.createContext();
我们创建的新 context 提供我们访问一对 Provider 和 Consumer 。 Provider 为我们提供在整个 React 树中共享状态变化的能力。 Consumer 允许我们在树中的任何位置订阅这些状态更改。
context
Provider
Consumer
我们刚刚创建的 Context 有一个名为 Provider 的静态类方法,它是一个 React 组件。 该组件接受 value 属性。 这非常重要,因为这个属性代表我们需要传递给树中更下层组件的全局状态。 在我们的例子中,我们想要全局共享的是 stage 属性和 handleClick 方法。
通过使用我们在本系列的第一部分中使用的 props.children 技术,我们可以动态地将任何子组件暴露给 Provider,无论它在组件树中有多深。
props.children
class StepperProvider extends Component { state = { stage: 1 } render() { return ( <StepperContext.Provider value={{ stage: this.state.stage, handleClick: () => this.setState({ stage: this.state.stage + 1 }) }}> {this.props.children} </StepperContext.Provider> ) } }
通过简单地用 StepperProvider 组件包裹原来的 Stepper 组件,树下的所有子组件现在都暴露在我们的 Context 中。
StepperProvider
class App extends Component { render() { return ( <StepperProvider> <Stepper stage={1}> <Stepper.Progress> <Stepper.Stage num={1} /> </Stepper.Progress> <Stepper.Steps> <Stepper.Step num={1} text={"Stage 1"}/> </Stepper.Steps> </Stepper> </StepperProvider> ); } }
我们的 Stepper 代码几乎没有变,只是将它包裹在 StepperProvider 组件中,现在我们所有的子组件都可以访问 stage 和 handleClick,而无需手动将它们向下传递到每个组件。
最初,我们的状态由 Stepper 组件管理,我们克隆了每个子组件来接收所需的 props。
class Stepper extends Component { state = { stage: this.props.stage } static defaultProps = { stage: 1 } static Progress = Progress static Steps = Steps static Stage = Stage static Step = Step handleClick = () => { this.setState({ stage: this.state.stage + 1 }); } render() { const { stage } = this.state; const children = React.Children.map(this.props.children, child => { return React.cloneElement(child, {stage, handleClick: this.handleClick}) }) return ( <div style={styles.container}> {children} </div> ); } }
上面的这些代码几乎都不再需要了。 我们不再需要创建状态,我们不再需要传递任何 props。 我们完全可以废弃这些代码,只保留我们声明的静态方法,来对外提供一个干净可读的API。
class Stepper extends Component { static Progress = Progress static Steps = Steps static Stage = Stage static Step = Step render() { return ( <div style={styles.container}> {children} </div> ); } } export default Stepper;
我将使用 Stepper.Step 组件来演示如何连接 Consumer 组件。 以前,Stepper.Step 组件需要其父级直接传递 stage 属性以使其正常运行:
Stepper.Step
export const Step = ({num, text,stage}) => ( return stage === num ? <div key={num} style={styles.stageContent}>{text}</div> : null )
随着我们的应用程序连接 Context,我们可以使用 Consumer 来订阅它:
<Consumer> {value => /* render something based on the context value */} </Consumer>
Consumer 需要一个函数作为子项,此函数提供我们全局的 Context 值。函数完成后,返回一个 react 节点。
究竟是什么意思?
起初它有点令人头疼,但让我们来看看“消费”的 Step 组件。
Step
export const Step = ({num, text}) => ( <StepperContext.Consumer> {value => { const {stage} = value return stage === num ? <div key={num} style={styles.stageContent}>{text}</div> : null }} </StepperContext.Consumer> )
我们不是直接将 Step 标记作为子项添加到 Consumer 中,而是添加一个函数。 这个函数提供了我们之前在 Provider 创建的值,然后我们可以使用 ES6 解构来提取 stage 属性。Step 组件现在可以像以前一样访问 stage 属性,只是这一次是从 Context 中获取的。 在这里我们可以随意的使用它; 我们使用它来确定返回什么 React 节点。
这里使用的技术可能看起来有点奇怪。 它被称为 Render Props,官方 react 文档的解释。 这是一个非常强大的技术,我将在本系列的第3部分中探讨。
Render Props
到这里我不再逐步详细介绍了,只需要对 Stepper.Steps,Stepper.Progress 和 Stepper.Stage 组件重复第4和第5步骤,您最终应该看到组件的外观和功能与以前完全相同。
Stepper.Progress
Stepper.Stage
现在,我们任何组件都不依赖于其他组件的直接后代。 我们现在有更灵活的代码,应该能够添加我们之前无法做到的标题了!
Stepper.Steps 和 Stepper.Step 不再直接从父级那里取出 stage属性。 他们从 Context 订阅它,所以额外的 div 不会阻止 props 在组件树下进一步传递。 该应用仍然如期运行!
这么做给了我们很大的灵活性。 我们可以重用我们的组件来动态创建 Stepper 组件的复杂变体,而不必担心我们的应用结构是否被破坏
虽然我们可以在应用程序中的任何地方使用此组件,但它仍然不是真正可重用的。我们仍然需要 Context 的引用才能使其工作。
在本系列的下一部分中,我将探讨如何使用 render props 来实现相同的目标,而不必依赖于连接 Context 来共享应用程序中组件之间的状态。
render props
The text was updated successfully, but these errors were encountered:
No branches or pull requests
原文链接:How To Master Advanced React Design Patterns: Context API
使用高级设计模式创建灵活可重用的React组件 - 第1部分:复合组件
在本系列的上一部分中,我们探讨了如何使用复合组件和静态类方法来创建灵活可重用的组件。使用我们创造的API,我们能够以声明的方式来动态重建各种变化的组件
我们可以轻松添加任意数量的
step
,我们可以决定progress
是在左侧还是右侧。我们能够做到这一点是因为我们使用了一些 React API 辅助函数将所需的属性传递给组件树中的每个子项;
stage
和handleClick
属性可被需要它们的组件访问。但是这种技术存在一个主要缺陷。
props
只能传递给他们的直接子项。 这使得 API 非常僵硬,它要求Stepper.Steps
组件必须是Stepper
组件的直接子组件,否则props
传递会中断。 这在灵活性上存有巨大影响。如果我们想要使用 flexbox 添加标题怎么办?
通过添加一个简单的 div ,我们完全破坏了组件。
Stepper.Steps
组件不再是Stepper
组件的直接子组件,因此无法接收其props
。那有没有一种灵活的,仅需要小调整就能达到我们预期的方法呢?
答案就是:
Context
!!React Context API
React Context
已经存在了一段时间,但 React 工程师非常清楚它是实验性的,并且很可能在不久的将来会废弃。 好消息的是从 React 16.3 开始,它已经稳定了,我们可以在整个 React 应用程序中使用它。那么我们一直听到的这个
Context
是什么?我无法给出比 React 官方文档更清晰的定义:
这正好解决我们的问题! 使用
Context
,我们不再需要遍历并克隆每个子项来传递所需的props
。Context
的设计让我们可以共享“全局”状态,并在 React 树中任何位置获取。接着,让我展示给你如何使用和运行
Context API
的步骤。1.创建新的 Context
React 现在带有一个名为
createContext
的方法。 我们需要做的只是调用此方法并将其赋给一个变量。我们创建的新
context
提供我们访问一对Provider
和Consumer
。Provider
为我们提供在整个 React 树中共享状态变化的能力。Consumer
允许我们在树中的任何位置订阅这些状态更改。2.创建 Context Provider
我们刚刚创建的
Context
有一个名为Provider
的静态类方法,它是一个 React 组件。 该组件接受 value 属性。 这非常重要,因为这个属性代表我们需要传递给树中更下层组件的全局状态。 在我们的例子中,我们想要全局共享的是stage
属性和handleClick
方法。通过使用我们在本系列的第一部分中使用的
props.children
技术,我们可以动态地将任何子组件暴露给Provider
,无论它在组件树中有多深。3.用 Provider 包裹 Stepper 组件包
通过简单地用
StepperProvider
组件包裹原来的Stepper
组件,树下的所有子组件现在都暴露在我们的Context
中。我们的
Stepper
代码几乎没有变,只是将它包裹在StepperProvider
组件中,现在我们所有的子组件都可以访问stage
和handleClick
,而无需手动将它们向下传递到每个组件。4.重构我们的组件
最初,我们的状态由
Stepper
组件管理,我们克隆了每个子组件来接收所需的props
。上面的这些代码几乎都不再需要了。 我们不再需要创建状态,我们不再需要传递任何
props
。 我们完全可以废弃这些代码,只保留我们声明的静态方法,来对外提供一个干净可读的API。5.使用 Consumer 订阅状态更改
我将使用
Stepper.Step
组件来演示如何连接Consumer
组件。 以前,Stepper.Step
组件需要其父级直接传递stage
属性以使其正常运行:随着我们的应用程序连接
Context
,我们可以使用Consumer
来订阅它:Consumer
需要一个函数作为子项,此函数提供我们全局的Context
值。函数完成后,返回一个 react 节点。究竟是什么意思?
起初它有点令人头疼,但让我们来看看“消费”的
Step
组件。我们不是直接将
Step
标记作为子项添加到Consumer
中,而是添加一个函数。 这个函数提供了我们之前在Provider
创建的值,然后我们可以使用 ES6 解构来提取stage
属性。Step
组件现在可以像以前一样访问stage
属性,只是这一次是从Context
中获取的。 在这里我们可以随意的使用它; 我们使用它来确定返回什么 React 节点。这里使用的技术可能看起来有点奇怪。 它被称为
Render Props
,官方 react 文档的解释。 这是一个非常强大的技术,我将在本系列的第3部分中探讨。6.对需要 props 或传递 props 的所有组件重复第4和第5步骤
到这里我不再逐步详细介绍了,只需要对
Stepper.Steps
,Stepper.Progress
和Stepper.Stage
组件重复第4和第5步骤,您最终应该看到组件的外观和功能与以前完全相同。现在,我们任何组件都不依赖于其他组件的直接后代。 我们现在有更灵活的代码,应该能够添加我们之前无法做到的标题了!
Stepper.Steps
和Stepper.Step
不再直接从父级那里取出stage
属性。 他们从Context
订阅它,所以额外的 div 不会阻止 props 在组件树下进一步传递。 该应用仍然如期运行!这么做给了我们很大的灵活性。 我们可以重用我们的组件来动态创建
Stepper
组件的复杂变体,而不必担心我们的应用结构是否被破坏虽然我们可以在应用程序中的任何地方使用此组件,但它仍然不是真正可重用的。我们仍然需要
Context
的引用才能使其工作。在本系列的下一部分中,我将探讨如何使用
render props
来实现相同的目标,而不必依赖于连接Context
来共享应用程序中组件之间的状态。The text was updated successfully, but these errors were encountered: