Search code examples
javascriptreactjsreduxreact-reduxreact-color

Color picker from react-color doesn't work properly


I'm using react-color and this tutorial to implemented a ChromePicker.

How I'm expecting it ti work:

  • open the ChromePicker
  • select a color using the hue bar and/or the "big square"
  • after the ChromePicker is closed I want to update the color value on an object that is on props

So far it works like this:

  • it opens when its button is clicked
  • selecting the color is not working properly. When I click on the hue and drag that small circle sideways the color is changing ( I know this because there is a this.state.color) but the circle remains the same position. Also, I would expect to load on the "big square" the now color palette but it's not doing it. If I click in the square to select a color, it works similar to the hue bar. The circle remains on the same position even if the value is changing.
  • the value is saved but also doesn't work fine: that request is called multiple times, every time the color is changed. I want it to change only after I select a color and I exit the ChromePicker (by clicking somewhere outside of it) and I was thinking that onChangeComplete would do that.

Here is a screenshot of it:

enter image description here

Why is the ChromePicker so badly? Is there a way to solve it? And also, I would like to save the new color on Company only after the ChromePicker is closed (it seems that onChangeComplete is not triggered by that)

This is the code:

 import React from 'react';
    import { Button, Icon } from 'semantic-ui-react';
    import { ChromePicker } from 'react-color';
    import { connect } from 'react-redux';
    import { Creators } from '../../actions';
    
    
    class Banner extends React.PureComponent {
      constructor(props) {
        super(props);
        this.state = {
          displayColorPicker: false,
        };
      }
    
      handleClick = () => {
        this.setState({ displayColorPicker: true });
      };
    
      handleClose = () => {
        this.setState({ displayColorPicker: false });
      };
    
      handleChangeComplete = colors => {
        const {
          name,
          color,
        } = this.state;
        this.setState({ color: colors.hex });
        const { updateCompany } = this.props; // company is the entity from props that is updated
                                              // it contains 2 values, its name and its color
        updateCompany(this.props.company._id, {
          name,
          color,
        });
      };
    
    
    
      render() {
        this.props.color.color.color = this.state.color;
        const popover = { // this is not essential, it's some styling for the picker
          position: 'absolute',
          zIndex: '2',
        };
        const cover = { // same as for popover
          position: 'fixed',
          top: '0px',
          right: '0px',
          bottom: '0px',
          left: '0px',
        };
        const {company } = this.props; // gets the company from props
        return (
          <div className="banner-container settings-banner">   
     
      //the below div with its style is updating in real time when the color is changed 
                  <div style={{ backgroundColor: this.state.color }}>              
                    <div>
                      <Button
                        className="select-color-btn"
                        onClick={this.handleClick}>
                        Select a color
                      </Button>
                      {this.state.displayColorPicker ? (
                        <div style={popover}>
                          <div
                            style={cover}
                            onClick={this.handleClose}
                            onKeyDown={this.handleClick}
                            role="button"
                            tabIndex="0"
                            aria-label="Save"
                          />
                          <ChromePicker
                            color={this.props.company.color}
                            onChangeComplete={this.handleChangeComplete}
                          />
                        </div>
                      ) : null}
                    </div>
                  </div>
            
          </div>
        );
      }
    }
    
    const mapStateToProps = state => ({
      company: state.companies.selectedCompany,
    });
    
    const mapDispatchToProps = {
      updateCompany: Creators.updateCompanyRequest,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Banner);

Solution

  • In order to make the ChromePicker work (moving the hue bar and the circle inside the "big square" it must be added onChange function and glue it to onHandleChange.

    Also, inside onHandleChangeComplete, the color must not be taken from the state but from function's parameter, colors:

    import React from 'react'; import { Button, Icon } from 'semantic-ui-react'; import { ChromePicker } from 'react-color'; import { connect } from 'react-redux'; import { Creators } from '../../actions';

    class Banner extends React.PureComponent {
      constructor(props) {
        super(props);
        this.state = {
          displayColorPicker: false,
        };
      }
    
      handleClick = () => {
        this.setState({ displayColorPicker: true });
      };
    
      handleClose = () => {
        this.setState({ displayColorPicker: false });
      };
    
      handleChange2 = colors => {
        this.setState({ background: colors.hex });
     };
    
      handleChangeComplete = colors => {
        this.setState({ background: colors.hex });
        const {
          name,
        } = this.state;
        const color = colors.hex;
        this.setState({ color: colors.hex });
        const { updateCompany } = this.props;                                           
        updateCompany(this.props.company._id, {
          name,
          color,
        });
      };
    
    
    
      render() {
        this.props.color.color.color = this.state.color;
        const popover = { // this is not essential, it's some styling for the picker
          position: 'absolute',
          zIndex: '2',
        };
        const cover = { // same as for popover
          position: 'fixed',
          top: '0px',
          right: '0px',
          bottom: '0px',
          left: '0px',
        };
        const {company } = this.props; // gets the company from props
        return (
          <div className="banner-container settings-banner">   
                  <div style={{ backgroundColor: this.state.color }}>              
                    <div>
                      <Button
                        className="select-color-btn"
                        onClick={this.handleClick}>
                        Select a color
                      </Button>
                      {this.state.displayColorPicker ? (
                        <div style={popover}>
                          <div
                            style={cover}
                            onClick={this.handleClose}
                            onKeyDown={this.handleClick}
                            role="button"
                            tabIndex="0"
                            aria-label="Save"
                          />
                          <ChromePicker
                            color={this.state.background}
                            onChange={this.handleChange2} 
                            onChangeComplete={this.handleChangeComplete}
                          />
                        </div>
                      ) : null}
                    </div>
                  </div>
            
          </div>
        );
      }
    }
    
    const mapStateToProps = state => ({
      company: state.companies.selectedCompany,
    });
    
    const mapDispatchToProps = {
      updateCompany: Creators.updateCompanyRequest,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Banner);