Setting Up Actions in React?


#1

Thanks to the Ken Rogers book I’m moving along very well in getting up to speed on Meteor/Mantra/React. I’ve gotten routing, containers and components working and now I’m setting up my first actions.

It seems like I’ve done everything I’m supposed to do with regard to actions, but I’m still getting this console error:

export const depsMapper = (context, actions) => ({
create: actions.myCanines.create, <==ERROR: CANNOT READ PROPERTY ‘CREATE’ OF UNDEFINED
clearErrors: actions.myCanines.clearErrors,
context: () => context
});

It’s bound to be some super-newbie thing but I’ve been over sample code from Ken and others and haven’t yet spotted what I’m leaving out. What am I missing?

FOLDER LAYOUT

modules directory
–myCanines directory
----actions directory
--------index.js
--------myCanines.js
----container directory
--------newmyCanine.js
--------myCaninesList.js
----components directory
--------myCaninesList.jsx
--------newmyCanine.jsx
----index.js

FILE CONTENTS
modules directory
–myCanines directory
----index.js:

import actions from ‘./actions’;
import routes from ‘…/core/routes.jsx’;
export default {
routes,
actions
};

----actions directory
------index.js

import myCanines from './myCanines';
export default {
    myCanines
};

----actions directory
------myCanines.js

export default {
    create({Meteor, LocalState, FlowRouter}, name) {
        if (!name) {
            return LocalState.set('CREATE_Canine_ERROR', 'Job Specialty name is required.');
        }
        LocalState.set('CREATE_Canine_ERROR', null);
        Meteor.call('myCanines.create', name, (err) => {
            if (err) {
                return LocalState.set('SAVING_ERROR', err.message);
            }
        });
        FlowRouter.go('/myCanines');
    },
    clearErrors({LocalState}) {
        return LocalState.set('SAVING_ERROR', null);
    }
};

----container director
------newmyCanine.js

import NewCanine from '../components/newmyCanine.jsx';
import {useDeps, composeWithTracker, composeAll} from 'mantra-core';
export const composer = ({context, clearErrors}, onData) => {
    const {LocalState} = context();
    const error = LocalState.get('CREATE_CATEGORY_ERROR');
    onData(null, {error});
    // clearErrors when unmounting the component
    return clearErrors;
};
export const depsMapper = (context, actions) => ({
    create: actions.myCanines.create,
    clearErrors: actions.myCanines.clearErrors,
    context: () => context
});
export default composeAll(
    composeWithTracker(composer),
    useDeps(depsMapper)
)(NewCanine);

Note-- my app isn’t about dogs; I’ve just replaced my file/variable names with ‘canines’ for purposes of this post. :slight_smile:

Thanks very much in advance to all for any info!


#2

What does your /client/main.js file look like?


#3

/client/main.js:

import {createApp} from ‘mantra-core’;
import initContext from ‘./configs/context’;

// modules
import coreModule from ‘./modules/core’;
import caninesModule from ‘./modules/myCanines’;

// init context
const context = initContext();

// create app
const app = createApp(context);
app.loadModule(coreModule);
app.loadModule(caninesModule);
app.init();


#4

I think in actions/index.js you need to do:

import myCanines from './myCanines';
export default {
    ...myCanines // destructure default import object
};

#5

I tried this per your suggestion, but it did not yet resolve the anomaly.


#6

I found it. In my client/main.js file, I wasn’t declaring the module with the actions. What a newbie error!


#7

@vikr00001 Hey there, How did you solve the problem exacly?


#8

If you are using Mantra, use Mantra-CLI to create a new action. This sets up all kinds of necessary declarations for you. :slight_smile:


#9

Oke, I am quite new on mantra and I loaded mantra-core as a npm package “mantra-core”: “^1.2.0”,

What is the mantra-CLI for actions? Currently, my actions are not passed as a prop from container to component.

Cant solve this problem also.


#10

That is not the correct way to set up a new project using Meteor and Mantra. Use Mantra-CLI to create your empty Meteor/Mantra folder.


#11

This is how I’m using Mantra, hopefully helpful. Not sure if its 100% to spec, it’s just the way I’ve personally been doing things. :slight_smile:

// actions/index.js:

import SomeActions from './some_actions';
export default {
  SomeActions
}
// actions/some_actions.js:

export default {

  toggleState({LocalState}) {
    const currentState = LocalState.get('TOGGLE');
    LocalState.set('TOGGLE', !currentState);
  },

  checkId(context, id) {
    // Some logic
    // return something
  }

}
// containers/some_container.js:

// composer function

export const depsMapper = (context, actions) => ({
  toggleState: actions.SomeActions.toggleState,
  checkId: id => actions.SomeActions.checkId(id),
  context: () => context
});

// export composeAll 

// components/some_view.jsx:

import React from 'react';

class SomeView extends React.Component {
  constructor(props) {
    super(props);
    this.toggleState = this.props.toggleState.bind(this);
    this.checkId = this.props.checkId.bind(this);
  }

  render() {
    return (
      <div>
        <div onClick={this.toggleState}></div>
        <div>{this.checkId(123) ? something : null}</div>
      </div>
    )
  }
}


#12

I reinitiated my learning code to mantra-cli and I am still getting the same error I was getting without the cli.

The error is that when I try to add an action and call it with my component after implementing it as a props with container, I am getting this $ is not a function error at the console.

I used some console.log’s and there is nothing in my this.props currently, so I believe something is preventing the container to transfer useDeps() to component.

Cant figure it out also.

Also, I really liked the mantra-cli


#13

The codes that I have

export default {
    create: function({Meteor, LocalState, FlowRouter}, email, password) {
        if (!email) {
            return LocalState.set('CREATE_USER_ERROR', 'Email is required.');
        }

        if (!password) {
            return LocalState.set('CREATE_USER_ERROR', 'Password is required.');
        }

        LocalState.set('CREATE_USER_ERROR', null);

        Accounts.createUser({email, password});
        FlowRouter.go('/');
    },

    login({Meteor, LocalState, FlowRouter}, email, password) {
        if (!email) {
            return LocalState.set('LOGIN_ERROR', 'Email is required.');
        }

        if (!password) {
            return LocalState.set('LOGIN_ERROR', 'Password is required.');
        }

        LocalState.set('LOGIN_ERROR', null);

        Meteor.loginWithPassword(email, password);
        FlowRouter.go('/');
    },

    clearErrors({LocalState}) {
        return LocalState.set('SAVING_ERROR', null);
    }
};



import {useDeps, composeAll, composeWithTracker, compose} from 'mantra-core';

import NewUser from '../components/new_user.jsx';

export const composer = ({context, clearErrors}, onData) => {
    const {Meteor, Collections} = context();
    const {LocalState} = context();
    const error = LocalState.get('CREATE_USER_ERROR');

    onData(null, {error});
};

export const depsMapper = (context, actions) => ({
    context: () => context,
    create: actions.NewUser.create,
    clearErrors: actions.NewUser.clearErrors
});

export default composeAll(
  composeWithTracker(composer),
  useDeps(depsMapper)
)(NewUser);
import React from 'react';
import {Col, Panel, FormControl, Button} from 'react-bootstrap';
class NewUser extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        const {error} = this.props;
        return (
            <Col xs={12} sm={6} smOffset={3}>
                <Panel>
                    <h1>Register</h1>
                    {error ? <p style={{color: 'red'}}>{error}</p> : null}
                    <form>
                        <FormControl ref="email" type="email" placeholder="Email" />
                        <FormControl ref="password" type="password" placeholder="Password" />
                        <Button onClick={this.createUser.bind(this)}
                                bsStyle="primary"
                                type="submit">
                            Sign Up
                        </Button>
                    </form>
                </Panel>
            </Col>
        );
    }
    createUser(e) {
        e.preventDefault();
        const {create} = this.props;
        const {email, password} = this.refs;
        console.log(this.props);
        create(email.value, password.value);
        email.value = '';
        password.value = '';
    }
}
export default NewUser;
> import actions from './actions';
> import routes from './routes.jsx';

> export default {
>   routes,
>   actions,
>   load(context) {
>     
>   }
> };

In the example of @joshig, which binds the props.actions inside constructur, my browser gives the error for binding.

Cannot read property ‘bind’ of undefined


#14

Where are you defining the create action?


#15

Edited the top post.


#16

If you’re passing data to the create function it should be like my example I think:
create: (email, password) => actions.NewUser.create(email, password)


#17

@joshig Still same error, as the props are empty, I think it is not a problem with returning data back to container. But you are right, that was another error :slight_smile:

I uploaded entire code to github, as I told earlier, I am using it to get familiar with Mantra but not working for me.

https://github.com/musyilmaz/mantra-problem


#18

@musyilmaz I found your problem:

/client/modules/users/routes.jsx

You’re calling the component not the container it should be this:

import React from 'react';
import {mount} from 'react-mounter';

import MainLayout from '/client/modules/core/components/main_layout.jsx';
// <---- OLD
// import NewUser from '/client/modules/users/components/new_user.jsx';
// -----
import NewUser from '/client/modules/users/containers/new_user.js';
// NEW ---->

export default function (injectDeps, {FlowRouter}) {
  const MainLayoutCtx = injectDeps(MainLayout);

  FlowRouter.route('/register', {
    name: 'users.new',
    action() {
      mount(MainLayoutCtx, {
        content: () => (<NewUser />)
      });
    }
  });
}

This is why no props were being passed.

:slight_smile:

PS. You should upgrade your Mantra version from 1.2 to 1.5


#19

Thanks, that makes sense :slight_smile:

I loaded component because in the guide, they loaded home page component (which is basically a prop free render only), I thought, it pulls the container with it.

Amazing solution, btw I cannot update my mantra version also