包装受控的反应组件,以允许组件消费者省略特定的Prop/处理程序对。不可控制的使您可以以最小的状态编写React组件,然后将它们包裹在组件中,该组件将在排除prop/handler的情况下管理状态。
npm i -S uncontrollable
如果您对此模块的原因有些不确定,请先阅读下一节。如果您只想看到一些现实世界的示例,请查看React小部件,以大量使用此策略。
import { uncontrollable } from 'uncontrollable' ;
useUncontrolledProp(value, defaultValue, onChange) => [value, onChange]
可以用来代替上述高阶组件的React钩子。它返回了一套完整的props
,这些道具可以安全地传播到子元素。
import { useUncontrolledProp } from 'uncontrollable' ;
const UncontrolledCombobox = ( { value , defaultValue , onChange } ) => {
// filters out defaultValue, defaultOpen and returns controlled
// versions of onChange, and onToggle.
const [ controlledValue , onControlledChange ] = useUncontrolledProp (
value ,
defaultValue ,
onChange
) ;
return < Checkbox { ... controlledProps } / > ;
} ;
useUncontrolled(props, propsHandlerHash) => controlledProps
可以用来代替上述高阶组件的React钩子。它返回了一套完整的props
,这些道具可以安全地传播到子元素。
import { useUncontrolled } from 'uncontrollable' ;
const UncontrolledCombobox = ( props ) => {
// filters out defaultValue, defaultOpen and returns controlled
// versions of onChange, and onToggle.
const controlledProps = useUncontrolled ( props , {
value : 'onChange' ,
open : 'onToggle' ,
} ) ;
return < Checkbox { ... controlledProps } / > ;
} ;
React的优势之一是其可扩展性模型,这是通过将组件状态尽可能高地推到树上的共同实践来实现的。虽然非常适合使组件非常灵活且易于推理,但它可以产生大量的样板,以使每次使用时将组件连接起来。对于简单的组件(例如输入),这通常是通过其onChange
处理程序将输入value
支架与父状态属性联系起来的问题。这是一个极为普遍的模式:
render ( ) {
return (
< input type = 'text'
value = { this . state . value }
onChange = { e => this . setState ( { value : e . target . value } ) }
/ >
)
}
这种模式将其管理value
从输入到其父母的责任和模仿“双向”数据指标。但是,有时,父母无需直接管理输入状态。在这种情况下,我们要做的就是设置输入的初始value
,并让输入从那时开始进行管理。 React通过“不受控制的”输入来处理这一点,如果您不表示要通过value
支架在外部控制输入状态,则只会为您提供书籍保存。
这是一个很好的模式,我们可以在自己的组件中使用。假设父母希望控制所有有意义的一切,通常最好构建每个组件以尽可能无状态。以简单的下拉组件为例
class SimpleDropdown extends React . Component {
static propTypes = {
value : React . PropTypes . string ,
onChange : React . PropTypes . func ,
open : React . PropTypes . bool ,
onToggle : React . PropTypes . func ,
} ;
render ( ) {
return (
< div >
< input
value = { this . props . value }
onChange = { ( e ) => this . props . onChange ( e . target . value ) }
/ >
< button onClick = { ( e ) => this . props . onToggle ( ! this . props . open ) } >
open
< / button >
{ this . props . open && (
< ul className = "open" >
< li > option 1 < / li >
< li > option 2 < / li >
< / ul >
) }
< / div >
) ;
}
}
请注意,我们如何在简单的下拉列表中不跟踪任何状态?这很棒,因为我们的模块的消费者将拥有所有灵活性来决定下拉菜单的行为。另外,请注意我们的公共API(PropTypes),它由共同的模式组成:我们想要设置的属性( value
, open
),以及一组指示我们何时设置的处理程序( onChange
, onToggle
)。由父组件决定对处理程序的响应更改value
和open
道具。
尽管这种模式为消费者提供了极好的灵活性,但它还要求他们编写一堆样板代码,这些代码可能不会因使用而变化太大。他们很可能总是想对onToggle
进行open
,并且只有在极少数情况下,才能覆盖该行为。这是受控/不受控制的模式进出的地方。
如果消费者不提供open
道具(表明他们想控制它),我们只想自己处理开放/onToggle案例。我们可以通过将下拉列表和包装在另一个为我们处理的组件中包装,而不是使我们的下拉组件与所有逻辑造成所有逻辑的复杂性,而是掩盖了我们的下拉逻辑。
uncontrollable
可让您分开创建受控/不受控制的输入所需的逻辑,使您专注于创建一个完全控制的输入并稍后再包装。这也往往更简单地推理。
import { uncontrollable } from 'uncontrollable' ;
const UncontrollableDropdown = uncontrollable ( SimpleDropdown , {
value : 'onChange' ,
open : 'onToggle'
} )
< UncontrollableDropdown
value = { this . state . val } // we can still control these props if we want
onChange = { val => this . setState ( { val } ) }
defaultOpen = { true } / > // or just let the UncontrollableDropdown handle it
// and we just set an initial value (or leave it out completely)!
现在,我们不需要担心开放的Ontoggle!返回的组件将通过假设应将其open
到任何onToggle
返回的情况下为我们跟踪open
。如果我们想担心它,我们可以提供open
和onToggle
道具,而未控制的输入只会通过它们。
以上是一个人为的示例,但它使您可以包装更复杂的组件,从而为您提供了很多灵活性,可以为您提供组件的消费者。对于每对Prop/处理程序,您还会获得“默认[PREPNAME]”表单的默认设备,因此value
- > defaultValue
和open
> defaultOpen
等。react Widgets大量使用此策略,您可以在操作中看到它这里:https://github.com/jquense/react-widgets/blob/5D1B530CB094CDC72F577FE01ABE4A02DD265400/src/src/multiselect.jsx#l521