Rafael
Rafael

Reputation: 68

Complex nested components on React not working

I'm beginner with React, I already looked all around and couldn't find an explanation, all the examples were with basic simple components so wouldn't match with mine.

The first thing was, I was trying to make an SPA with React, and the basic wireframe is:

<HEADER> 
<CONTENT> 
<FOOTER>

with only the content changing over the pages changes.

So to avoid repeat <HEADER> and <FOOTER> in every component page I found the solution that makes an template with the header and footer there, and render the content as children of the template, so I got this TEMPLATE:

<HEADER>
    {this.children}
<FOOTER>

and the PAGE became:

<TEMPLATE>
    {my content here}
</TEMPLATE>

with that everything worked good except that now all the binds in my content stopped to work, so any of my inputs fields works.

Anyone knows how to help me? I would appreciate if anyone could explain me why it doesn't work and how to fix, or how to restructure my project so I can get my desired functionality.

Thanks!

Here follows my structure:

(The problem is on the render method of the Perfil component, none of the fields show their value or allow the value to change)

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import * as ServiceWorker from './serviceWorker';
import Routes from './router';

import './index.css';
ReactDOM.render(
    <Routes />, document.getElementById('root')
);

ServiceWorker.register();
// router.js
import React from 'react';
import {BrowserRouter, Switch, Route} from 'react-router-dom';

//Pages
import Home from './component/home.component';
import Contact from './component/contact.component';
import Perfil from './component/perfil.component';
import About from './component/about.component';
import NotFound from './component/notfound.component';

const Routes = () => (     
    <BrowserRouter>
        <Switch>
            <Route exact path="/"  component={Home} />
            <Route path="/contact"  component={Contact} />
            <Route path="/perfil"  component={Perfil} />
            <Route path="/about"  component={About} />
            <Route component={NotFound} />
        </Switch>
    </BrowserRouter>
);
export default Routes;
// Perfil module
import React, { Component } from 'react';
import axios from 'axios';

import TemplateA from '../../TemplateA';
import apiUrl from '../../axiosApi';
import './index.css';

class Perfil extends Component{

    constructor(props){
      super(props);

      this.state = {
        loaded:'0',
        message:'',
        name: '',
        email: '',
        phone: '',
      };
    }

    changeHandler = e => {
      this.setState({[e.target.name]: e.target.value})
    }

    getHttp = () =>{
        apiUrl.get(`/user/1/`).then( res => {
            this.setState({
                loaded:1,
                message:'',
                name: res.data.nome,
                email: res.data.email,
                phone: res.data.phone,            
            });
        })
        .catch( error => {
            console.log("error");
            console.log(error);
            this.setState({"message": error});
        });
    }

    componentDidMount() {
      this.getHttp();  
    }

    render() {
      const {name, email, phone } = this.state;

      console.log(name);  // here works
      /*
          After the return this.state neither the declared const works.
      */

      return(
        <TemplateA>
          <div className="formPerfil">
            {
              this.state.menssage !== '' ?  (
                <span color="danger">{this.state.mensagem}</span>
              ):''
            }
            <h2>Perfil</h2>
            <hr />
            <h3>Your data</h3>
            <form name="ddP">
              <fieldset>
                <input type="text" placeholder="Name" required
                    name="name" value={this.state.name} onChange={this.changeHandler}/> 
                <input type="email" placeholder="[email protected]" required
                    name="email" value={email} onChange={this.changeHandler}/>
                <input type="text" placeholder="21900000000" maxLength="11" required
                    name="phone" value={phone} onChange={this.changeHandler} /> <br/>
                <br/>
                <input type="submit" value="Update" />
              </fieldset>
            </form>
          </div>
        </TemplateA>
      );
    }

}

export default Perfil;
// TemplateA
import React, { Component } from 'react';

import Header from './template/header'
import Footer from './template/footer'
import './TemplateA.css'

class TemplateA extends Component{

  constructor(props) {
    super(props);
    this.children = props.children;
  }

  render(){
    return (
      <div className="App">
        <Menu/>
          {this.children}
        <Footer/>
      </div>
    );
  };

};

export default App;

Upvotes: 2

Views: 582

Answers (1)

Nicholas Tower
Nicholas Tower

Reputation: 84982

class TemplateA extends Component{

  constructor(props) {
    super(props);
    this.children = props.children;
  }

  render(){
    return (
      <div className="App">
        <Menu/>
          {this.children}
        <Footer/>
      </div>
    );
  };

};

The only time you are interacting with the props is in the constructor. If the props change, the constructor is not going to be rerun, so you will be rendering with the original children not the new ones.

There is no need to save this.children. Just use props directly in render:

render() {
  return (
    <div className="App">
      <Menu/>
        {this.props.children}
      <Footer/>
    </div>
  );
};

Upvotes: 2

Related Questions