首页 > 解决方案 > 反应上下文道具钻,我没有得到什么?

问题描述

底部有一个 TL;DR。

我可能做错了,或者以不好的方式使用了上下文。我是新手,所以我不知道这是否是我们应该做的事情。

我的理解:
上下文可用于将 props 传递给嵌套更深的子组件,而无需通过所有级别的嵌套。提供者充满了道具,消费者将“向上查找”以找到最近的提供者并获取它的数据。
如果是这种情况,那么我可以加载具有函数的提供程序,例如 onChange 处理程序,以避免在每个子组件都执行相同操作时必须在每个子组件上编写处理程序。这将允许“智能表单”通过“传递”给它的处理程序来管理其输入的处理程序。显然,仅仅在多个组件上编写一个处理程序不是问题,但是拥有大约 20-30 个表单字段并在每个组件上编写 4 个以上的处理程序只会造成代码混乱。所以我尝试了以下方法:

HTML结构是这样的,例如:

<ControlledForm data={some_data} handlers={some_handlers}>
    <LabeledControl name="Type your name" rel="Name" meta={{some_meta_object}}></LabeledControl>
    <LabeledControl name="Pet name" rel="PetName" meta={{some_meta_object}}></LabeledControl>
    <LabeledControl name="Type of pet" rel="PetType" meta={{some_meta_object}}></LabeledControl>
    <LabeledControl name="Family" rel="Family" meta={{some_meta_object}}></LabeledControl>
</ControlledForm>

这是ControlledForm类代码:

const { Provider } = React.createContext(); //Note this

class ControlledForm extends Component {
    state = {};

    render() {
        return (
            <Provider value={{ onChange: this.props.onChange }}>
                <form>{this.props.children}</form>
            </Provider>
        );
    }
}

现在,无论我放在这个表单中的任何孩子都希望<Consumer>在它周围有一个包装来使用changeHandler,或者至少这是计划。但是,当我将 my 包装LabeledControl在消费者中时,它就像没有数据一样。

<LabeledControl>(简化代码):

const { Consumer } = React.createContext();

class LabeledControl extends Component {
    state = {};
    render() {
        return (
            <Consumer>
                {r => {
                    console.log("consumer:", r); //Logs undefined
                    return (
                        <div className="labeled-control">
                            {/*Code here*/}
                        </div>
                    );
                }}
            </Consumer>
        );
    }
}

如果我要猜测问题出在哪里,我会说这是因为 theControlledFormLabeledControlcreate 它都有自己的上下文,不共享,请查看上面的代码。但我不明白我将如何共享此上下文并仍将这两个类保存在单独的 .js 文件中。我不能将参考传递给孩子们,我得到的只是{this.props.children}并且没有办法告诉它“嘿,在这里使用这个提供者”。我在网上找到的所有示例在同一个文件中都有两个类,即提供者和消费者,能够引用相同的“上下文”,但这严重影响了我可以在表单中放入内容的自由,或者更确切地说不会不要让我在“孩子”方面进行定制。

TLDR

当它们位于两个不同的 javascript 文件中时,如何将“上下文”从提供者传递给消费者?代码在上面。我基本上需要将一个处理程序传递给每个孩子并让它(也许,也许不是,取决于孩子)使用处理程序告诉父母更新它的数据。所有这些都{this.props.children}在父组件中使用,以允许“外部代码”向父组件“注入”任何所需的子组件,并让它们使用或不使用父处理程序。

编辑:

我搜索了一下,发现了两种可能的解决方案,我都对其进行了测试,并且似乎都可以正常工作(使用有限的用例)。渲染 props和React.CloneElement似乎都可以在只有一层嵌套时发挥作用,因为我们可以使用它们直接渲染和添加 props 给子级,但是当我们需要钻几个层级时,中间的所有组件都必须实现相同的道具传递,然后变成意大利面条代码。仍在寻找尝试找到将上下文传递给孩子以在不同文件中使用的方法。

标签: javascriptreactjs

解决方案


请查看下面的代码。

另外:这是我建立的一个示例项目:https ://codesandbox.io/s/5z62q8qnox

import React from 'react'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';

export default class ControllerForm extends React.Component {
  static childContextTypes = {
    onChange: PropTypes.func.isRequired
  }
  getChildContext() {
    return {
      onChange: this.handleOnChange
    }
  }
  handleOnChange = (e) => {
    console.log(e.target.value) //here is the place you have to implement
  }
  render() {
    return (
      <div class="container">
        {this.props.children}
      </div>

    )
  }

}

import React from 'react'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class LabeledControl extends React.Component {

static contextTypes ={
  onChange : PropTypes.func.isRequired
}

  render() {

    return (

      <div>
        <div className="form-group">
          <input className="form-control" type="text" onChange={this.context.onChange} />
      </div>
      </div>
    )
  }

}

function App() {
  return (
    <div className="App">
      <ControllerForm>
        <LabeledControl />
        <LabeledControl />
      </ControllerForm>
    </div>
  );
}

推荐阅读