最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to make React HOC - High Order Components work together? - Stack Overflow

programmeradmin2浏览0评论

I have a small presentation ponent like this:

function MyList({data, uppercaseMe, lowercaseMe}) {
    return <ul>
        {data.map(item =>
            <li>{item} -
                <button onClick={() => uppercaseMe(item)}>Uppercase me!</button>
                <button onClick={() => lowercaseMe(item)}>Lowercase me!</button>
            </li>)}
    </ul>;
}

And then I have three HOCs I would like to use to decorate MyList:

const WithData = (Component) => {
return class extends React.Component {

    constructor(props) {
        super(props);
        this.state= {data:['one', 'two', 'three', 'four', 'five']};
    };

    render() {
        return <Component {...this.props} data={this.state.data} />;
    }
}
};

const WithUppercase = (Component) => {
return class extends React.Component {

    uppercaseMe = (item) => {
        // how to access WithData state from here?
        console.log(item.toUpperCase());
    };

    constructor(props) {
        super(props);
    };

    render() {
        return <Component {...this.props} uppercaseMe={this.uppercaseMe}/>;
    }
 }
};

const WithLowercase = (Component) => {
return class extends React.Component {

    lowercaseMe = (item) => {
        // how to access WithData state from here?
        console.log(item.toLowerCase());
    };

    constructor(props) {
        super(props);
    };

    render() {
        return <Component {...this.props} lowercaseMe={this.lowercaseMe}/>;
    }
 }
};

Like this:

const ComposedList = WithLowercase(WithUppercase(WithData(MyList)));

Unfortunately, I don't know how to change the state of WithData from within WithLowercase or WithUppercase.

How do you expose the state of one HOC to the other HOCS in the chain?

I have a small presentation ponent like this:

function MyList({data, uppercaseMe, lowercaseMe}) {
    return <ul>
        {data.map(item =>
            <li>{item} -
                <button onClick={() => uppercaseMe(item)}>Uppercase me!</button>
                <button onClick={() => lowercaseMe(item)}>Lowercase me!</button>
            </li>)}
    </ul>;
}

And then I have three HOCs I would like to use to decorate MyList:

const WithData = (Component) => {
return class extends React.Component {

    constructor(props) {
        super(props);
        this.state= {data:['one', 'two', 'three', 'four', 'five']};
    };

    render() {
        return <Component {...this.props} data={this.state.data} />;
    }
}
};

const WithUppercase = (Component) => {
return class extends React.Component {

    uppercaseMe = (item) => {
        // how to access WithData state from here?
        console.log(item.toUpperCase());
    };

    constructor(props) {
        super(props);
    };

    render() {
        return <Component {...this.props} uppercaseMe={this.uppercaseMe}/>;
    }
 }
};

const WithLowercase = (Component) => {
return class extends React.Component {

    lowercaseMe = (item) => {
        // how to access WithData state from here?
        console.log(item.toLowerCase());
    };

    constructor(props) {
        super(props);
    };

    render() {
        return <Component {...this.props} lowercaseMe={this.lowercaseMe}/>;
    }
 }
};

Like this:

const ComposedList = WithLowercase(WithUppercase(WithData(MyList)));

Unfortunately, I don't know how to change the state of WithData from within WithLowercase or WithUppercase.

How do you expose the state of one HOC to the other HOCS in the chain?

Share Improve this question asked Aug 4, 2017 at 20:30 SrgHartmanSrgHartman 6512 gold badges8 silver badges23 bronze badges 0
Add a ment  | 

2 Answers 2

Reset to default 7 +50

You don't want to expose the state directly. What you do want to do is pass down a function as a prop to the wrapped ponent that will allow you to update state.

So if we take the HOC and add a little bit of code, creating a function to update its state that can now be passed down to its child ponents:

const WithData = (Component) => {
  return class extends React.Component {

      constructor(props) {
          super(props);
          this.state= {data:['one', 'two', 'three', 'four', 'five']};
      }

      update (data) {
        this.setState({ data });
      }

      render() {
          return <Component onUpdate={this.update} {...this.props} data={this.state.data} />;
      }
  }
};

Now within one of the child ponents we can consume it:

const WithUppercase = (Component) => {
    return class extends React.Component {

        uppercaseMe = (item) => {
            const itemIndex = this.props.data.indexOf(item);
            const newData = this.props.data.slice();
            newData[itemIndex] = item.toUpperCase();
            this.props.onUpdate(newData); // update WithData state here
        };

        constructor(props) {
            super(props);
        };

        render() {
            // onUpdate function is once again passed to child ponents
            // implicitly through props if you need to call it again
            return <Component {...this.props} uppercaseMe={this.uppercaseMe}/>;
        }
    }
};

Passing data back up the chain of ponents essentially goes against the pattern that react uses, which is one way data flow. Data goes down but never goes back up. Like @FuriousD said you should make the lowerCase and upperCase methods available to ponents further down the chain. You can do this using context.

"In some cases, you want to pass data through the ponent tree without having to pass the props down manually at every level. You can do this directly in React with the powerful "context" API."

Here is an example using an updated version of your code.

const WithUpperCase = (Component) => {
  class _WithUpperCase extends React.Component {
    getChildContext() {
      return {
        uppercaseMe: this.uppercaseMe,
      }
    }

    uppercaseMe = (item) => {
      return item.toUpperCase()
    };

    render() {
      return <Component {...this.props} />;
    }
  }

  _WithUpperCase.childContextTypes = {
    lowercaseMe: PropTypes.func,
  }

  return _WithUpperCase;
};

const WithLowercase = (Component) => {
  class _WithLowercase extends React.Component {
    getChildContext() {
      return {
        lowercaseMe: this.lowercaseMe,
      }
    }

    lowercaseMe = (item) => {
      return item.toLowerCase()
    };

    render() {
      return <Component {...this.props} />;
    }
  }

  _WithLowercase.childContextTypes = {
    lowercaseMe: PropTypes.func,
  }

  return _WithLowercase;
};

function MyList({ data }, { uppercaseMe, lowercaseMe }) {
  return (
    <ul>
      {
        data.map(item => (
          <li>{item} -
          <button onClick={() => uppercaseMe(item)}>Uppercase me!</button>
            <button onClick={() => lowercaseMe(item)}>Lowercase me!</button>
          </li>
        ))
      }
    </ul>
  );
}

MyList.contextTypes = {
  // Need to import the PropTypes library
  uppercaseMe: PropTypes.func,
  lowercaseMe: PropTypes.func,
};
发布评论

评论列表(0)

  1. 暂无评论