Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React & Redux : connect() to multiple components & best practices

I'm working on my first React/Redux project and I have a little question. I've read the documentation and watched the tutorials available at https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-visibletodolist.

But I still have one question. It's about a login page. So I have a presentational component named LoginForm :

components/LoginForm.js

import { Component, PropTypes } from 'react'

class LoginForm extends Component {
   render () {
      return (
         <div>
            <form action="#" onSubmitLogin={(e) => this.handleSubmit(e)}>
               <input type="text" ref={node => { this.login = node }} />
               <input type="password" ref={node => { this.password = node }} />
               <input type="submit" value="Login" />
            </form>
         </div>
      )
   }

   handleSubmit(e) {
      e.preventDefault();
      this.props.onSubmitLogin(this.login.value, this.password.value);
   }
}

LoginForm.propTypes = {
   onSubmitLogin: PropTypes.func.isRequired
};

export default LoginForm;

And a container component named Login which pass data to my component. Using react-redux-router, I call this container (and not the presentationnal component) :

containers/Login.js

import { connect } from 'react-redux'
import { login } from '../actions/creators/userActionCreators'
import LoginForm from '../components/LoginForm'

const mapDispatchToProps = (dispatch) => {
   return {
      onSubmitLogin: (id, pass) => dispatch(login(id, pass))
   }
};

export default connect(null, mapDispatchToProps)(LoginForm);

As you can see, I'm using the connect method provide by redux to create my container.

My question is the following one :

If I want my Login container to use multiple views (for example : LoginForm and errorList to display errors), I need to do it by hand (without connect because connect take only one argument). Something like :

class Login extends Component {

   render() {
      return (
         <div>
            <errorList />
            <LoginForm onSubmitLogin={ (id, pass) => dispatch(login(id, pass)) } />
         </div>
      )
   }

}

Is it a bad practice ? Is it better to create another presentational component (LoginPage) which use both errorList and LoginForm and create a container (Login) which connect to LoginPage ?


EDIT: If I create a third presentational component (LoginPage), I'll have to pass data twice. Like this : Container -> LoginPage -> LoginForm & ErrorList. Even with context, it don't seems to be the way to go.

like image 469
Armelias Avatar asked Jan 27 '16 08:01

Armelias


People also ask

What is React used for?

It is a library that allows us to create native mobile apps in Android and iOS using React. js. In 2016, with version 15, React. js started using Semantic Versioning.

Is React for HTML or js?

To get an overview of what React is, you can write React code directly in HTML. But in order to use React in production, you need npm and Node. js installed.

Is React better or angular?

React is better than Angular due to it's virtual DOM implementation and rendering optimizations. Migrating between React's versions is quite easy, too; you don't need to install updates one by one, as in the case of Angular. Finally, with React, developers have myriads of existing solutions they can use.

What is in React means?

1 : to exert a reciprocal or counteracting force or influence —often used with on or upon. 2 : to change in response to a stimulus. 3 : to act in opposition to a force or influence —usually used with against. 4 : to move or tend in a reverse direction. 5 : to undergo chemical reaction.


1 Answers

I think that what you have in your second example is very close. You can create just one container component that's connected and render multiple presentational components.

In your first example, there actually isn't a separate container component:

import { connect } from 'react-redux'
import { login } from '../actions/creators/userActionCreators'
import LoginForm from '../components/LoginForm'

const mapDispatchToProps = (dispatch) => {
   return {
      onSubmitLogin: (id, pass) => dispatch(login(id, pass))
   }
};

// `LoginForm` is being passed, so it would be the "container"
// component in this scenario
export default connect(null, mapDispatchToProps)(LoginForm);

Even though it's in a separate module, what you're doing here is connecting your LoginForm directly.

Instead, what you can do is something like this:

containers/Login.js

import { connect } from 'react-redux'
import { login } from '../actions/creators/userActionCreators'
import LoginForm from '../components/LoginForm'
import ErrorList from '../components/ErrorList'

class Login extends Component {

   render() {
      const { onSubmitLogin, errors } = this.props;

      return (
         <div>
            <ErrorList errors={errors} />
            <LoginForm onSubmitLogin={onSubmitLogin} />
         </div>
      )
   }

}

const mapDispatchToProps = (dispatch) => {
   return {
      onSubmitLogin: (id, pass) => dispatch(login(id, pass))
   }
};

const mapStateToProps = (state) => {
   return {
       errors: state.errors
   };
};

export default connect(mapStateToProps, mapDispatchToProps)(Login);

Note that the Login component is now being passed to connect, making it the "container" component and then both the errorList and LoginForm can be presentational. All of their data can be passed via props by the Login container.

like image 128
Nick Ball Avatar answered Oct 19 '22 03:10

Nick Ball