totalnoob
totalnoob

Reputation: 2741

converting react classes to functions with redux

I'm still new to react/redux, after getting something like this to function

User.js

class User extends React.Component {
    componentWillMount() {
        this.props.fetchUser(.....);
}

render() {
    return (
        <Profile />
    )
}

export default connect(null, {fetchUser})(User);

Profile.js

class Profile extends React.Component {
    render() {

        const { user } = this.props

        return (    
                <h1>{user.profile.name}</h1>
        )
}

const mapStateToProps = state => ({
    user: state.store.user
});

export default connect(mapStateToProps, {})(Profile)

actions.js

export const fetchUser = (.....) => dispatch => {
    fetch()
    .....
}

reducers.js

case FETCH_USER:
    return {
        ...state,
        user: action.payload.user
    };    

As I understand it, the User component calls an action (fetchUser) from connect on componentWillMount(). That action calls an api, gets the data and the reducer adds that to the store within the state. The Profile component can then use connect to map the data from fetchUser in the store and display that data.

After reading some tutorials including https://github.com/reactjs/redux/blob/master/docs/basics/UsageWithReact.md

It looks like things can be simplified a bit without using classes.

If I were to change the User and Profile components to a more functional way, how would I do it?

eg.

const User = () => {
    return (
        <Profile />
    )
}

how do I dispatch the fetchUser action and how do I simulate it to be called with the flow of componentWillMount()?

or am I just over complicating things?

Upvotes: 0

Views: 1783

Answers (5)

Subin Sebastian
Subin Sebastian

Reputation: 10997

There is also a way to support lifecycle methods in functional components. https://www.npmjs.com/package/react-pure-lifecycle

import React from 'react';
import lifecycle from 'react-pure-lifecycle';

 // create your lifecycle methods
const componentDidMount = (props) => {
   console.log('I mounted! Here are my props: ', props);
};

// make them properties on a standard object
const methods = {
  componentDidMount
};

const FunctionalComponent = ({children}) => {
return (
  <div>
    {children}
  </div>
 );
};

// decorate the component
export default lifecycle(methods)(FunctionalComponent);

Upvotes: 3

RIYAJ KHAN
RIYAJ KHAN

Reputation: 15290

I think,User component is designed nicely.It will act as a container for Profile to provide the Data.

Instead of making Profile component class oriented,it should be Stateless.

Lets User component pass the required data for Profile component. You don't need to connect Profile component using redux-connect.Just render it as a Child component of User.

Profile

const Profile = (props) => {

  const {user, likeProfile} = props;
  //likeProfile()//call like this using dom event or programmatically.
  return (
    <h1>{user.profile.name}</h1>
  )
}

You need to make some changes in User component. Get the state for Profile component via mapStateToProps.

class User extends React.Component {
  componentWillMount() {
      this.props.fetchUser(.....);
}

render() {

  const {user, likeProfile} = this.props;
  return (
      <Profile user= {user} likeProfile={likeProfile} /> //passed the user data to Profile component vua User
  )
}

Map the user state for Profile in User connect.

const mapStateToProps = (state)=>{
  return{
    user : state.somereducerkey.user //this will be accessible in Profile via props { user}
  } 
}
export default connect(mapStateToProps, {fetchUser, likeProfile})(User);

Upvotes: 1

Gopinath Kaliappan
Gopinath Kaliappan

Reputation: 7369

In Your case you can continue with statefull components no wrong in that ,If you need to go with functional way

There is a work arround

https://github.com/mobxjs/mobx/issues/162

Suggestion

Calling the api in componentDidMount will make sense than componentWillMount , Because you can show the user something is fetching.

Upvotes: 1

Eric
Eric

Reputation: 2705

  1. React recommends that ajax request be made in componentDidMount(), rather than in componentWillMount(). For more info on this, read this post.

  2. Since you want to make ajax requests in componentDidMount(), you need a class. There are two ways of writing component definitions: functional component and the class component. Functional components are more concise, but you don't get component lifecycle methods like componentDidMount(). Think of it as just a render function that takes props as inputs and outputs DOMs (in JSX). To override those lifecycle methods, you need to define them as a class.

  3. If you want to use Redux, and want to make ajax requests in a Redux action, you should import the action creator function (fetchUser(..) in your case) that makes the ajax request, and dispatch(fetchUser(..)) in componentDidMount(). connect(..)ed components get dispatch(..) function passed to it by Redux store.

If you want to see how it's done in other redux apps, see the official example apps in the redux.js repo, paying attention to actions and containers: https://github.com/reactjs/redux/tree/master/examples

Upvotes: 1

Gavin Thomas
Gavin Thomas

Reputation: 1867

I think you should keep using statefull components with redux... https://medium.com/@antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4

Redux connect — is a PureComponent. Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store from a current context.

Same is working, for example, for styled-component — you can wrap it with PureComponent, but it will still react to Theme changes.

Solution is simple — bypass logic, use old school events bus, subcribe, wait and emit events.

Styled-componets:

componentWillMount() {      
// subscribe to the event emitter. This      
// is necessary due to pure components blocking 
// context updates, this circumvents      
// that by updating when an event is emitted.

   const subscribe = this.context[CHANNEL];
   this.unsubscribe = subscribe(nextTheme => {  <----- MAGIC
React-redux:

trySubscribe() {
 if (shouldSubscribe && !this.unsubscribe) {
  this.unsubscribe = 
     this.store.subscribe(this.handleChange);  <----- MAGIC
 }
}
componentDidMount() {
  this.trySubscribe();
}

Thus, even if parent Pure Component will block any update enables you to catch a change, store update, context variable change, or everything else.

So — something inside pure components is very soiled and absolutely impure. It is driven by side effects! But this bypass straight logic flow, and works just differently from the rest of application.

So — just be careful. And don’t forget about magic.

Aaaand…. And this is a reason, why any redux store update will cause redraw in each connected component, and why you should use reselect just next to connect HoC —

to stop unnecessary change propagation. But you should read this from another point of view:

redux-connect is a source of a change propagation. redux connect is the end of a change propagation. It is still PureComponent. And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.

Conclusion Pure components keep your application fast. Sometimes — more predictable, but often — less predictable, as long they change the way application works.

Stateless components are not pure, and may run slower than PureComponents by any kind.

But… if you very wish to create a fast application with good user experience — you have to use Pure Component.

No choice. But, now — you know hidden truth, and knew some magic…

Upvotes: 1

Related Questions