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

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

programmeradmin0浏览0评论

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 ponents relative to this question, the parent ponent 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='/' ponent={Intro} />
                <Route path='/register' ponent={Register} />
                <Route path='/login' ponent={Login}/>
                <Route path='/userpage' ponent={Userpage}/>
                <Route path='/science' ponent={Science}/>
                <Route path='/literature' ponent={Literature}/>

            </Switch>


        </BrowserRouter>

`

Then there is a child ponent 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 ponent and then down to the Header ponent, so I can render it on screen.

My understanding is that I am to make a callback from the parent ponent 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='/' ponent={Intro} />
                                        <Route path='/register' ponent={Register} />
                                        <Route path='/login' ponent={Login}/>
                                        <Route path='/userpage' ponent={Userpage}/>
                                        <Route path='/science' ponent={Science}/>
                                        <Route path='/literature' ponent={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.

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 ponents relative to this question, the parent ponent 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='/' ponent={Intro} />
                <Route path='/register' ponent={Register} />
                <Route path='/login' ponent={Login}/>
                <Route path='/userpage' ponent={Userpage}/>
                <Route path='/science' ponent={Science}/>
                <Route path='/literature' ponent={Literature}/>

            </Switch>


        </BrowserRouter>

`

Then there is a child ponent 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 ponent and then down to the Header ponent, so I can render it on screen.

My understanding is that I am to make a callback from the parent ponent 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='/' ponent={Intro} />
                                        <Route path='/register' ponent={Register} />
                                        <Route path='/login' ponent={Login}/>
                                        <Route path='/userpage' ponent={Userpage}/>
                                        <Route path='/science' ponent={Science}/>
                                        <Route path='/literature' ponent={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.

Share Improve this question edited Aug 6, 2020 at 20:30 HoldOffHunger 21k11 gold badges120 silver badges146 bronze badges asked Aug 25, 2019 at 8:48 Constantine BlackConstantine Black 2035 silver badges18 bronze badges 7
  • passing user data parent to child or child to parent – prasanth Commented Aug 25, 2019 at 8:52
  • @prasanth I think I must make a callback from the parent to the child to pass data from child(Register.js) to parent(App.js) and then down from parent to child(Header). Don't know if even this is somehow messed up. Thanks. – Constantine Black Commented Aug 25, 2019 at 8:57
  • You can't you have a state let's say Users and then set the Users state after the axios registration and share that State with both <Register.js> and <WhateverComponent> ? – iwaduarte Commented Aug 25, 2019 at 9:02
  • @iwaduarte Hi. If I get correctly what you are saying: I am making a axios Post request, my user gets registered and then I load his username with success. Then-here is my difficulty- I need to pass(as I get it with a callback from parent(App.js)) the username data from the child to the parent and after that make it available to my header ponent for rendering on screen. Does that make any sense? Thank you. – Constantine Black Commented Aug 25, 2019 at 9:07
  • Yeah it does. That is what I am saying. In your parent App.js your state should be there shared with Register and Header. – iwaduarte Commented Aug 25, 2019 at 9:10
 |  Show 2 more ments

2 Answers 2

Reset to default 3

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)

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 plexity 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 ponent. Or you need that behaviour in other ponents 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 ponents, 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 ponent so it can be tested independently and reused. Hooks allow you to reuse stateful logic without changing your ponent hierarchy. This makes it easy to share Hooks among many ponents or with the munity.

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:

  • MobX (More on the level of redux)
  • unstated and unstated-next (they claim to have a simpler approach)
  • React Apollo (if using graphql)
发布评论

评论列表(0)

  1. 暂无评论