react-select: [v2.0.0-beta.2] Custom ValueContainer doesn't work without rendering "children"

I’m trying to use the ValueContainer option to render the selected option differently. Seems I cannot do that and still let the selected option (ValueContainer) be clickable and open the menu without also rendering the children prop. However, in rendering the children prop, I’m basically rendering the selected option twice. Clicking the arrow still works though.

Basically, this works fine (example from docs):

const ValueContainer = ({children, ...props}) => (
  <components.ValueContainer {...props}>{children}</components.ValueContainer>
);

This does not work:

const ValueContainer = ({children, ...props}) => {
  if (!props.hasValue) {
    return <components.ValueContainer {...props}>{children}</components.ValueContainer>;
  }

  const value = props.getValue()[0];
  return (
    <components.ValueContainer {...props}>
      <NodeItem node={value} />
    </components.ValueContainer>
  );
};

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 36
  • Comments: 30

Most upvoted comments

You could filter out the input from children so you can still keep standard functionality:

const ValueContainer = ({children, ...props}) =>{
    var selectInput = React.Children.toArray(children).find((input) => input.type.name === "Input" || input.type.name === "DummyInput");
   
    return <components.ValueContainer { ...props }>
        { /* Whatever you want to render */ }
        {selectInput}
   </components.ValueContainer>
}

We were also facing this problem for a while. And below is the working code for us.

  1. We added inputId prop to <Select> component like below:
<Select
          className='select'
          classNamePrefix='filter'
          inputId='clickableInput'
          isMulti
          components={{ ValueContainer }}
         ....
        />
  1. And below is our return from ValueContainer:
return (
      <components.ValueContainer {...props}>
       // code for some custom element if you need goes here
        {React.Children.map(children, (child) => {
          return child.props.id === 'clickableInput' ? child : null;
        })}
      </components.ValueContainer>
    );

@Rall3n I found that I also needed to add ‘w’ to the array. After building my project I noticed the name for the dummyInputs got converted to w.

This worked for me:

const childsToRender = React.Children.toArray(children).filter((child) => ['Input', 'DummyInput', 'Placeholder', 'w'].indexOf(child.type.name) >= 0);

@dpiotti I would not recommend to filter by type.name because of minification (as you experienced yourself). Instead filter by type and compare with components.

But I would now recommend to do reverse filtering. Instead of filtering for the components you want to keep, you filter for the components you do not want to keep and prohibit them from rendering.

const ValueContainer = ({children, ...props}) => <components.ValueContainer {...props}>
  {React.Children.map(children, (child) => (child && [components.SingleValue].indexOf(child.type) === -1) ? child : null)}
</components.ValueContainer>;

This also circumvents the problem that DummyInput is not exported with components.

The bug still exists in the version 3.0.8 An easy fix is to set the property blurInputOnSelect to true. The downside of it though is that it closes the drop-down every time the user makes a selection, even with the property closeMenuOnSelect set to false

I was able to get a workaround to work in the meantime. Note that this is for a select that has isSearchable=false so it’s using the dummy input component, but it may work with searchable selects with more CSS tweaks to hide the input component.

With this approach, we render the children that has the focus/blur events and just hide the children content. For non-searchable selects, the dummy input should already be hidden, so in this case we just need to hide the value.

const ValueWrapper = styled.div`
  .dummy-input-wrapper {
    .Select__single-value {
      display: none;
    }
  }
`;

  const ValueContainer = (props) => {
    const { selectProps: { value: { label } }, children } = props;
    return (
      <components.ValueContainer {...props}>
        <ValueWrapper>
          <span className="formatted-value">
            <span>{category}:</span>
            <LabelValue>{label}</LabelValue>
          </span>
          {/* This is a hack to make the entire select clickable,
            rather than just the dropdown selector */}
          <span className="dummy-input-wrapper">
            {children}
          </span>
        </ValueWrapper>
      </components.ValueContainer>
    );
  };

I am also having this exact issue, breaks onFocus and onBlur props on main <ReactSelect> component. Version 2.2.0

We were also facing this problem for a while. And below is the working code for us.

1. We added `inputId` prop to `<Select>` component like below:
<Select
          className='select'
          classNamePrefix='filter'
          inputId='clickableInput'
          isMulti
          components={{ ValueContainer }}
         ....
        />
1. And below is our return from ValueContainer:
return (
      <components.ValueContainer {...props}>
       // code for some custom element if you need goes here
        {React.Children.map(children, (child) => {
          return child.props.id === 'clickableInput' ? child : null;
        })}
      </components.ValueContainer>
    );

Thank you for this solution, it solved my problem but now I can’t see placeholder text. Could you please tell how to render placeholder if no values are selected.

hey @einarq did you know how to change the isRtl prop value different from the SelectContainer component?

Nope, haven’t tried that, sorry

This may or may not help but I found in my case it was the focused prop on the multivalue child that causes this.

let multiValueProps = { ...props }

multiValueProps.components = {
  Container: components.MultiValueContainer,
  Label: components.MultiValueLabel,
  Remove: components.MultiValueRemove
}

I had to add in multiValueProps.isFocused = false

Inside my custom ValueContainer is:

[(
        <components.MultiValue {...multiValueProps}>
            {content}
        </components.MultiValue>
), children[1]]

As children[1] is always the input.

So I assume you still need some kind of multi value in there or something which states it at least isn’t focused as it will be grabbing the focus over the input on click.

Would be nice to make this easier/get some clarification.