Constantine Black
Constantine Black

Reputation: 203

React: Passing data between components using Router in App.js(parent component)

I am trying to self-learn React using it on the front-end while using Laravel framework on the back-end. My goal at this point is to have a user Register through a form(which work just fine).

On the front-end, there are two components relative to this question, the parent component App.js where my '/' route is loaded automatically and all the other routes are called with react router like this:

`

<BrowserRouter>
        <div>
            {this.state.data_username}
        </div>
            <Header />
            
            <Footer />

            <Switch>
                <Route exact path='/' component={Intro} />
                <Route path='/register' component={Register} />
                <Route path='/login' component={Login}/>
                <Route path='/userpage' component={Userpage}/>
                <Route path='/science' component={Science}/>
                <Route path='/literature' component={Literature}/>

            </Switch>


        </BrowserRouter>

`

Then there is a child component Register.js where I am making an axios post call to register the user.

Goal: When the user gets registered, I want to pass the username(which the code gets successfully since I have it showing up in console) to the parent component and then down to the Header component, so I can render it on screen.

My understanding is that I am to make a callback from the parent component to the child, passing somehow a function or an object to the child, assign the value I need inside the children and the pass it down to the Header.

What I tried was:

<Route path='/register'  render={props=>(<><Register    {...callbackFromParents}/></>)}

but this did not work. But, most of the examples I have seen don't use the react-router for handling routes; in the cases they do, I cannot replicate the render method correctly in the Route code.

Here is my code:

import React, {Component} from 'react'
                    import ReactDOM from 'react-dom'
                    import {BrowserRouter, Route, Switch } from 'react-router-dom'
                    // import {Link} from 'react-router-dom'
                    import Header from './Header'
                    import Intro from './Intro'
                    import Register from './Register'
                    import Login from './Login'
                    import Userpage from './Userpage'
                    import Footer from './Footer'
                    import Science from './Science'
                    import Literature from './Literature'




                    class App extends Component {

                        constructor(props){
                            super(props);
                            this.state={
                                data_username:'Username'
                            }

                            this.username_Callback=this.username_Callback.bind(this)



                        }

                        username_Callback(usernamecallback){
                        this.setState({data_username:usernamecallback});

                    }



                        // username_attend(){
                        //     var username=data_username;
                        // }

                    // render={props=>(<><Register {...callbackFromParents}/></>)}



                        render(){
                            return(


                                <BrowserRouter>
                                <div>
                                    {this.state.data_username}
                                </div>
                                    <Header />
                                    
                                    <Footer />

                                    <Switch>
                                        <Route exact path='/' component={Intro} />
                                        <Route path='/register' component={Register} />
                                        <Route path='/login' component={Login}/>
                                        <Route path='/userpage' component={Userpage}/>
                                        <Route path='/science' component={Science}/>
                                        <Route path='/literature' component={Literature}/>

                                    </Switch>


                                </BrowserRouter>




                                )
                        }
                    }

                    ReactDOM.render(<App />, document.getElementById('app'))

and the Register.js :

import React, {Component} from 'react'
    // import {Link} from 'react-router-dom'
    import axios from 'axios'


    class Register extends Component{
        constructor(props){
            super(props)
            this.state={
                name: '',
                email:'',
                password:'',
                errors:[]
            }

                this.handleFieldChange = this.handleFieldChange.bind(this)
            this.handleCreateNewUser = this.handleCreateNewUser.bind(this)
            this.hasErrorFor = this.hasErrorFor.bind(this)
            this.renderErrorFor = this.renderErrorFor.bind(this)
            

        }


    handleFieldChange(event) {
        this.setState({
          [event.target.name]: event.target.value
        })
    }

    handleCreateNewUser(event) {
            event.preventDefault()

            const { history } = this.props

            const user = {
              name: this.state.name,
              email: this.state.email,
              password: this.state.password

            }

            axios.post('/api/register', user)
              .then(response => {
                console.log('Success',response.data.flag)
                console.log('Success',response.data.username)
                this.props.callbackFromParent(response.data.username)
                // redirect to the homepage
                history.push('/')
              })
              .catch(error => {
                this.setState({
                  errors: error.response.data.errors
                })
              })
          }




    hasErrorFor(field) {
            return !!this.state.errors[field]
          }

    renderErrorFor(field) {
        if (this.hasErrorFor(field)) {
          return (
            <span className='invalid-feedback'>
              <strong>{this.state.errors[field][0]}</strong>
            </span>
          )
        }
    }



    render(){

        return (

    <div className='container py-4'>
          <div className='row justify-content-center'>
            <div className='col-md-6'>
              <div className='card'>
                <div className='card-header'>Create new account</div>
                <div className='card-body'>
                  
                  <form onSubmit={this.handleCreateNewUser} >
                    
                    <div className='form-group'>
                      <label htmlFor='name'>User Name</label>
                      <input
                        id='name'
                        className={`form-control ${this.hasErrorFor('name') ? 'is-invalid' : ''}`}
                        name='name'
                        value={this.state.name}
                        onChange={this.handleFieldChange}
                      />
                    </div>

                    <div className='form-group'>
                      <label htmlFor='description'>Email</label>
                      <input
                        id='email'
                        className={`form-control ${this.hasErrorFor('email') ? 'is-invalid' : ''}`} 
                        name='email'
                        value={this.state.email}
                        onChange={this.handleFieldChange}
                      />
                    </div>

                    <div className='form-group'>
                      <label htmlFor='description'>Password</label>
                      <input
                        id='password'
                        className={`form-control ${this.hasErrorFor('password') ? 'is-invalid' : ''}`}
                        name='password'
                        value={this.state.password}
                        onChange={this.handleFieldChange}
                      />
                    </div>

                    <button type='submit' className='btn btn-primary'>Register</button>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </div>  
        
        )
      }
    }
    export default Register

I get that I probably make some starter mistake but this problem has been bothering me for some time.

Upvotes: 2

Views: 5873

Answers (2)

webwelten
webwelten

Reputation: 1593

in your case it might be beneficial to store that kind of data globally instead of passing it manually. This could be the next step in your react lerning journey.

Watch out, React gives developers so much freedom that you might stumble upon a lot of options to solve issues. That's why I listed two built-in ways first and added other libraries to check out later. They are adding a bit of complexity and have their own learning curve.

Even if you don't solve it using what I've provided, it could serve you as an overview and a direction for the future.

Why care about data storage in your app?

Imagine you need to pass more than just the name or you need it in more than one component. Or you need that behaviour in other components as well. Will you always want to implement it manually like that? How does someone (new to the code or future you) get a good overview over what's happening if those informations are spread across the app. And what if you decide to save parts of the data in the browser cache?

Which options are there?

React context

Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.

React documentation on Context

React Context is provided as part of React and might be the easiest/quickest way to explore data storage. Even if you decide for another solution, have a look at context and check how it's working. It's an important part of React anyway.

React Hooks

With Hooks, you can extract stateful logic from a component so it can be tested independently and reused. Hooks allow you to reuse stateful logic without changing your component hierarchy. This makes it easy to share Hooks among many components or with the community.

React documentation on Hooks

Here are example of implementations: State Management with Hooks

State Management with Hooks and Context

Redux library

Redux can be described in three fundamental principles:

Single source of truth: The state of your whole application is stored in an object tree within a single store.

State is read-only: The only way to change the state is to emit an action, an object describing what happened.

Changes are made with pure functions: To specify how the state tree is transformed by actions, you write pure reducers.

Redux documentation on the three core principles.

Other state libraries

A bunch of other libraries for state mangement have been published. The options above might be the most used ones but there are other popular options, such as:

Upvotes: 2

iwaduarte
iwaduarte

Reputation: 1700

You are in the right track. In order to pass the function through the Route you should do the following:

<Route path='/register'  render={props=><Register {...props} 
                          callbackFromParents= {this.username_Callback}}/>

and then in the Register.js in the axios response:

//Note the plural and not plural in your code
this.props.callbackFromParents(response.data.username)

Upvotes: 3

Related Questions