0

I'm new to react js and I need to get the state of the component to be accessed by another class, I encountered this problem because I'm using atomic design because writing everything in one component is turning to be a problem, here is my code: Headcomponent:

class Headcomponent extends React.Component{

  constructor (props) {

    super(props);
    this.state = {
      email: '',
      password: '',
      formErrors: {email: '', password: ''},
      emailValid: false,
      passwordValid: false,
      formValid: false,
      items: [],

    }
  }


    this.setState({formErrors: fieldValidationErrors,
                    emailValid: emailValid,
                    passwordValid: passwordValid
                  }, this.validateForm);
}

validateForm() {
  this.setState({formValid: this.state.emailValid && 
  this.state.passwordValid});
}


render(){
        return (
  <Form fields={this.props.form} buttonText="Submit" />
        );
    }
}


Headcomponent.propTypes = {
  form: PropTypes.array,
};

Headcomponent.defaultProps = {
  form: [
    {
      label: 'label1',
      placeholder: 'Input 1',
    },
    {
      label: 'label2',
      placeholder: 'Placeholder for Input 2',
    },
  ],
};

export default Headcomponent;

form.js

   const Form = props => (
      <form className="Form">
        {
          props.fields.map((field, i) => (<LabeledInput label={field.label} placeholder={field.placeholder} key={i}/>))
        }
        <Button text={props.buttonText} />
      </form>
    );

    Form.propTypes = {
      fields: PropTypes.arrayOf(PropTypes.object).isRequired,
      buttonText: PropTypes.string.isRequired,
    };

    export default Form;

LabeledInput.js (where I need to pass the state of my password)

const LabeledInput = props => (
  <div className={`form-group `} >
    <Label text={props.label} />
    <Input value={props.value} placeholder={props.placeholder} type="text" onChange={props.onChange} />
    <div class="valid-feedback">{props.errorMessage}</div>
    <small class="form-text text-muted">{props.exampleText}</small>
  </div>
);

LabeledInput.propTypes = {
  label: PropTypes.string.isRequired,
  placeholder: PropTypes.string,
  onChange: PropTypes.func.required,
  value: PropTypes.string.isRequired,
  exampleText: PropTypes.string,
  errorMessage: PropTypes.string,
};

export default LabeledInput;

How can I access state of headComponent in LabeledInput?

Josh Pittman
  • 7,024
  • 7
  • 38
  • 66
Gherbi Hicham
  • 2,416
  • 4
  • 26
  • 41
  • 2
    Possible duplicate of [How to make a shared state between two react components?](https://stackoverflow.com/questions/38901106/how-to-make-a-shared-state-between-two-react-components) – Gabriel Bleu Feb 07 '18 at 11:07
  • 1
    Pass the state of headComponent as a prop to form component and then to labelledcomponent. If the components are entirely not related you need to use redux to manage the state. – anoop Feb 07 '18 at 11:10
  • If my response has answered your question please accept the answer. If you are still having trouble let us know – Josh Pittman Feb 08 '18 at 09:01

5 Answers5

2

The most reasonable way is passing it down (The values of Headcomponent's state you need) as props:

Headcomponent.js

class Headcomponent extends React.Component {
  constructor (props) {
    super(props);

    this.state = {
      email: '',
      password: '',
      formErrors: {email: '', password: ''},
      emailValid: false,
      passwordValid: false,
      formValid: false,
      items: [],
    }
  }

  render() {
    return (
      <Form
        fields={this.props.form}
        formValid={this.state.formValid}  // from Headcomponent's state
        buttonText="Submit"
      />
    );
  }
}

Form.js

const Form = props => (
   <form className="Form">
     {
       props.fields.map((field, i) => (
         <LabeledInput
           label={field.label}
           formValid={props.formValid}  // from Headcomponent's state
           placeholder={field.placeholder}
           key={i}
         />
       ))
     }
     <Button text={props.buttonText} />
   </form>
 );

LabeledInput.js

 const LabeledInput = props => (
   <div className={`form-group `} >
     { props.formValid && 'This form is valid' }  // from Headcomponent's state
     <Label text={props.label} />
     <Input value={props.value} placeholder={props.placeholder} type="text" onChange={props.onChange} />
     <div class="valid-feedback">{props.errorMessage}</div>
     <small class="form-text text-muted">{props.exampleText}</small>
   </div>
 );

So if any time the Headcomponent's state is updated it will be propagated to the LabeledInput component

Jose Paredes
  • 3,882
  • 3
  • 26
  • 50
1

You can use props to achieve this.

Root Component:

<div>
  <child myState="hello"></child>
</div>

Child Component:

<div>
  <child2 myOtherProperty={this.props.myState}></child2>
</div>

Child1 Component:

<div>{this.props.myOtherProperty}</div>

You can also pass functions as property to other components and let them call back the the root component if needed like so:

<div>
  <child onChange={this.myOnChangeMethodDefinedInRootComponent.bind(this)}></child>
</div>

this way you can "tell" the root components if something changed inside the children without using Redux

hope this helps

messerbill
  • 5,499
  • 1
  • 27
  • 38
1

Here is a quick prototype of what you are looking at,

passing state from head component as props to all the way down to label compoent. Changes in label component will modify the state of head component and force to re-render all components.

// Head Component
class HeadCompoent {
  constructor() {
    this.state = {
      password: '',
      userName: '',
    }
  }

  handleFieldChange = (key, val) => {
    this.setState({
      [key]: val,
    });
  };

  render() {
    <Form
      fields={[{
        item: {
          value: this.state.password,
          type: 'password',
          key: 'password'
        },
      }, {
        item: {
          value: this.state.userName,
          type: 'text',
          key: 'userName'
        }
      }]}
      handleFieldChange={this.handleFieldChange} 
    />
  }
}

// Form Component
const Form = (fields) => (
  <div>
    {
      fields.map(p => <Label {...p} />)
    }
  </div>);


// Label Component
const Label = ({ type, value, key, handleFieldChange }) => {
  const handleChange = (key) => (e) => {
    handleFieldChange(key, e.target.value);
  };

  return (
    <input type={type} value={value} key={key} onChange={handleChange(key)} />
  );
};
anoop
  • 3,229
  • 23
  • 35
1

The simplest way to access the state of headComponent in LabeledInput in to keep passing it down.

If you want to access the value this.state.password from headComponent inside LabeledInput then you pass this this.state.password as a prop to the form component in the render method

   render(){
        return (
          <Form 
            fields={this.props.form} 
            buttonText="Submit" 
            password={this.state.password} />
        );
    }

This then gives you access to this.state.password as a prop inside the Form component. You then repeat the process and pass it to the LabeledInput component inside form

const Form = props => (
      <form className="Form">
        {
          props.fields.map((field, i) => (
            <LabeledInput 
                label={field.label} 
                placeholder={field.placeholder} 
                key={i}
                password={this.props.password}
             />))
        }
        <Button text={props.buttonText} />
      </form>
    );

This then gives you access to the value inside the LabeledInput component by calling this.props.password.

Josh Pittman
  • 7,024
  • 7
  • 38
  • 66
  • There is another question actually, it's related to this one so I don't really want to open another question for it, do I do the same if I want to pass an action to my input in labeled input? Is passing down a function the same as passing down data? – Gherbi Hicham Feb 08 '18 at 09:06
  • 1
    Yes, you can pass down functions in the same way. Be careful how you call the function though. If you just put the function in an onClick it will fire when the page loads `onClick={this.props.actionName}` . You usually don't want that to happen so put it in a callback, that wayt it only fires when you click `onClick={() => this.props.actionName}` – Josh Pittman Feb 08 '18 at 10:59
-1

export default headComponent then import it inside LabelledInout

This is the Headcomponent

export default class Headcomponent extends React.Component{ 
  ...
}

LabelledInput Component

 import Headcomponet from './headComponent.js'

 const LabelledInput = (props) => {
    return(<Headcomponent />);
 }
Isaac Sekamatte
  • 5,500
  • 1
  • 34
  • 40
  • 2
    and how to get the reference to the correct instance of the component? – messerbill Feb 07 '18 at 11:05
  • You would have to create an instance of this, and then create a method to access to the state. You can't reference the state directly, the creation of a component is handled by `react` – Jose Paredes Feb 07 '18 at 11:08
  • @messerbill to get the instance you create a variable to store the instance. say `const head = new headComponent()` then you can access class methods on the instance. But if you only require to work with a class and not the component methods, why do you extend the Component class? – Isaac Sekamatte Feb 07 '18 at 11:14