简体   繁体   中英

The most “react” way to pass state to other components

I am fairly new to React. Currently I have two React components - Article.js and ControlForm.js

My render return in Article.js is this:

  return (
    <div className="article">
      {article_wrapper.map( article =>
        <div key={article.node.nid} className="article-main-display">
       <h1 className="title" dangerouslySetInnerHTML={createMarkup(article.node.title)}/>
          <div className="img-div"><img src={article.node.field_image.src} /></div>

          <ControlForm />    

          <div dangerouslySetInnerHTML={createMarkup(article.node.field_user_hsk_level)} />;
          <div className="field-name-field-chinese">
          <div dangerouslySetInnerHTML={createMarkup(article.node.chinese)} />;
                  </div>
        </div>
      )}
    </div>
  );

The ControlForm.js has several form elements (all of which I'd like to be able to pass along if need be), but this is the main one:

        <div className="form-item form-type-select form-group">
          <label className="control-label">Font Size</label>
          <select
            value={this.state.value}
            onChange={this.handleSizeSelect}
            id="font-size"
            className="form-control form-select"
          >
            <option value="0">Small</option>
            <option value="1">Normal</option>
            <option value="2">Large</option>
            <option value="3">XL</option>
          </select>
        </div>

I'd like to be able to set a class on one of the divs in the Article.js based on changing a value in the ControlForm.js

What is the most "React" way to do this? Would creating a common parent to both be the best method (right now, their only parent in common is the main App.js)

Sorry if I don't totally understand how this is supposed to work - this is my first React app.

The class associated with the components are ControlForm and withFetching respectively.

EDIT - the demo example below works, but I have some additional issues with how to integrate it properly into my existing code. Here's the existing functions of ControlForm:

class ControlForm extends Component {
  constructor() {
    super();
    this.state = { toggleActive: false, sizeSelect: "0", speed: 1.3, volume: .6};
    this.onToggle = this.onToggle.bind(this);
    this.handleSpeedChange = this.handleSpeedChange.bind(this);
    this.handleVolumeChange = this.handleVolumeChange.bind(this);
    this.handleSizeSelect = this.handleSizeSelect.bind(this);
  }


  onToggle() {
    this.setState({ toggleActive: !this.state.toggleActive });
  }

    handleSizeSelect(event) {
    this.setState({ sizeSelect: event.target.value });
    this.setState({font: 'large-font'});
    parentMethod(event.target.value);
  }

  handlePlayClick(e) {
    e.preventDefault();
    voice.playButtonClick();

  }
  handlePauseClick(e) {
    e.preventDefault();
    voice.pauseButtonClick();

  }
  handleStopClick(e) {
    e.preventDefault();
    voice.stopButtonClick();

  }
  handleVolumeChange(event) {
      console.log(event.target.value);
    this.setState({ volume: event.target.value });
  }
  handleSpeedChange(event) {
      console.log(event.target.value);
    this.setState({ speed: event.target.value });
  }

Articles looks like this:

const withFetching = (url) => (Comp) =>
  class WithFetching extends Component {
    constructor(props) {
      super(props);

      this.state = {
        data: [],
        isLoading: false,
        error: null,
        dynamicClassName: "parentClass"
      };
      this.changeClassName = this.changeClassName.bind(this);
    }

      changeClassName(childData) {
    this.setState({
      dynamicClassName: childData
    });
  }

    componentDidMount() {
      this.setState({ isLoading: true });

      fetch(url)
        .then(response => {
          if (response.ok) {
            return response.json();
          } else {
            throw new Error('Something went wrong ...');
          }
        })
        .then(data => this.setState({ data, isLoading: false }))
        .catch(error => this.setState({ error, isLoading: false }));
    }

    render() {
        //return "test";
      return <Comp { ...this.props } { ...this.state } />
    }
  }

function createMarkup(html) {
  return {__html: html};
}

      function changeClassName(childData) {
          console.log("GETS HERE!")
    this.setState({
      dynamicClassName: childData
    });
  }

    const Articles = ({ data, isLoading, error }) => {
        console.log(data);
        console.log(isLoading);
      const article_wrapper = data.nodes || [];

      if (error) {
        return <p>{error.message}</p>;
      }

      if (isLoading) {
        return <p>Loading ...</p>;
      }

      return (
        <div className="article">
          {article_wrapper.map( article =>
            <div key={article.node.nid} className="article-main-display">
           <h1 className="title" dangerouslySetInnerHTML={createMarkup(article.node.title)}/>
              <div className="img-div"><img src={article.node.field_image.src} /></div>

              <ControlForm parentMethod={changeClassName} />

              <div dangerouslySetInnerHTML={createMarkup(article.node.field_user_hsk_level)} />;
              <div className="field-name-field-chinese">
              <div dangerouslySetInnerHTML={createMarkup(article.node.chinese)} />;
                      </div>
            </div>
          )}
        </div>
      );
    }

    export default withFetching(API)(Articles);

Sorry about all of these questions, I know a lot of this is due to unfamiliarity with React - this is the first thing I've tried to build in React

You want to change parents from his childs.

First, you have to create a handler function at Article.js and pass it to ControlForm.js as a property. <ControlForm changeDiv={this.changeDiv} />

Then you focus on ControlForm.js, whenever you want to happen, you just execute the function you passed as a the prop changeDiv, like this.props.changeDiv()

See also possible duplicate: How to update parent's state in React?

Article.js ,

 class Article extends React.Component { constructor(props) { super(props); this.state = { dynamicClassName: "parentClass" } this.changeClassName = this.changeClassName.bind(this); } changeClassName(childData) { this.setState({ dynamicClassName: childData }); } // user dynamicClassName wherever u want . return ( < div className = "article" > { article_wrapper.map(article => < div key = { article.node.nid } className = "article-main-display" > < h1 className = "title" dangerouslySetInnerHTML = { createMarkup(article.node.title) } /> < div className = "img-div" > < img src = { article.node.field_image.src } /></div > < ControlForm parentMethod={this.changeClassName} / > < div dangerouslySetInnerHTML = { createMarkup(article.node.field_user_hsk_level) } />; < div className = "field-name-field-chinese" > < div dangerouslySetInnerHTML = { createMarkup(article.node.chinese) } />; < / div > < /div> ) } < /div> ); } 

In ControlForm js ,

 class ControlForm extends React.Component { constructor(props) { super(props); this.state = { } this.handleSizeSelect= this.handleSizeSelect.bind(this); } handleSizeSelect() { this.props.parentMethod(this.state.value); } render() { return ( <div className="form-item form-type-select form-group"> <label className="control-label">Font Size</label> <select value={this.state.value} onChange={this.handleSizeSelect} id="font-size" className="form-control form-select" > <option value="0">Small</option> <option value="1">Normal</option> <option value="2">Large</option> <option value="3">XL</option> </select> </div> ); } } 

you can conditionally render a class based on state and your handler was missing the values from the event on the onChange

here's a demo of dynamically changing style base on the state demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM