mantrajs / mantra-sample-blog-app

A sample blog app built with Mantra
http://mantra-sample-blog-app.herokuapp.com/
MIT License
296 stars 104 forks source link

State in actions #79

Open lyquocnam opened 8 years ago

lyquocnam commented 8 years ago

can i edit component state in actions ( like altjs... reflux) ? best solution on component is FORM with onChange method ? i want use stateless for this, can be ?

i'm done with put onChange on component:

onDataChange(e) { let item = this.state.item; switch (e.target.name) { case 'name': item.set({name: e.target.value}); this.props.actions._setErrors(null); break; } this.setState({item: item}); }

but i want put them on actions. it will right place i think ! and i swich...case on each column.... so many code, have any solution for this ?

Full class code:

class CategoryForm extends React.Component {

    componentWillReceiveProps({item}, state){
        this.setState({item: item});
    }

    onDataChange(e) {
        let item = this.state.item;
        switch (e.target.name) {
            case 'name':
                item.set({name: e.target.value});
                this.props.actions._setErrors(null);
                break;
        }
        this.setState({item: item});
    }

    render (){
        const {loading, actions, context} = this.props;

        if (loading || !this.state.item)
            return <Loading />

        const {name} = this.state.item;
        console.log(name);

        return (
            <div className="content-wrapper">

                <h3>Thông tin nhóm hàng</h3>
                <hr/>
                <Error errors={actions._getErrors()} />
                <form onSubmit={actions._submit.bind(this, this.state.item)}>

                    <Input
                        type="text"
                        name="name"
                        label="Tên nhóm hàng"
                        placeholder="Nhập tên nhóm hàng"
                        value={name}
                        onChange={this.onDataChange.bind(this)} />

                    <Button type="submit" bsStyle="primary">
                        <i className="fa fa-save"></i>Lưu
                    </Button>

                </form>
            </div>
        )
    }

}

export default CategoryForm;

thanks

sandervanhooft commented 8 years ago

Hi, Why don't you want to go stateless?

lyquocnam commented 8 years ago

yes, i want use stateless for simple and sorter code. but i dont know how to handle onChange on Input with stateless. i use 1 form for new + edit. i find simplier way for this.

sandervanhooft commented 8 years ago

Take a look at this example, and how the onClick event is bound to the createPost function.

There is no <form /> used. This.props is used instead of state. The {create} is an action. It is injected by the container.

To include this component in another component, you'd have to import the container instead of the component, while still using in the parent component.

Hope this helps, I'm also figuring things out as I go :).

import React from 'react';

class NewPost extends React.Component {
  render() {
    const {error} = this.props;
    return (
      <div className="new-post">
        <h2>Add New Post</h2>
        {error ? <p style={{color: 'red'}}>{error}</p> : null}

        <input ref="titleRef" type="Text" placeholder="Enter your post title." /> <br/>
        <textarea ref="contentRef" placeholder="Enter your post content." /> <br/>
        <button onClick={this.createPost.bind(this)}>Add New</button>
      </div>
    );
  }

  createPost() {
    const {create} = this.props;
    const {titleRef, contentRef} = this.refs;

    create(titleRef.value, contentRef.value);
  }
}

export default NewPost;
lyquocnam commented 8 years ago

yeah, i saw this example, but how we create edit_post form ? and only 1 form for create and edit.

sandervanhooft commented 8 years ago

Ok. So if the intended question here is: "how can I use a single component for both editing and creating an item?", then I would:

  1. pass the mode (new / edit) into the component as a prop
  2. pass the field values into the component using props (which are empty if mode ==='new)
  3. render the component accordingly, bind button to save function inside component
  4. save function inside component decides whether to update an existing item or create a new one depending on the mode (new or edit). Provide an action for each via the container.

You could omit setting the "mode", for example by checking if this.props.item._id is set.

sandervanhooft commented 8 years ago

(If that's the question you intended then it's a good one by the way - thank you for letting me structure my thoughts on this :) )

lyquocnam commented 8 years ago

ya, but i don't know how to create edit form with stateless ? and i want only once form for create and edit. (React require onChange on input, else it will readonly...)

sandervanhooft commented 8 years ago

It's no problem to use state in the component locally as far as I know. Only for external dependencies/consequences you should omit state.

dovuongnguyen commented 8 years ago

@lyquocnam you can do like this component handleChange(event, index, value){ const {setFilter} = this.props; console.log(value); setFilter('projectId', value); }, render() {const {filter} = this.props; <SelectField onChange={this.handleChange} value={filter.projectId}>}

container export const composerProject = ({context,projectId}, onData) => { const {Meteor, Collections, LocalState} = context(); const filter = LocalState.get('FILTERS'); if (!filter) LocalState.set('FILTERS', {projectId: projectId}) onData(null, {filter}); }; action export default { setFilter({LocalState},variable, value) { let filter = LocalState.get('FILTERS'); filter[variable] = value; return LocalState.set('FILTERS',filter); } };

lyquocnam commented 8 years ago

thanks @dovuongnguyen i understand this, but my problem is stateless with onchange on input. :3 i dont' know simplier for my code. are you vietnamese ?

lyquocnam commented 8 years ago

ya ! i find a solution to put onChange of input on actions.

Component:

                    <Input
                        name="name"
                        label="Tên nhóm hàng"
                        placeholder="Nhập tên nhóm hàng"
                        value={name}
                        onChange={actions._formChange.bind(this, {form: this})} />

and actions:

_formChange(props, {form}, e){
    const {item} = form.state;

    switch (e.target.name) {
      case 'name':
        item.set({name: e.target.value});
        self._clearErrors();
        break;
    }

    form.setState({item: item});
  },

just send class object to action. any better good way ? if can use stateless here it will be better i think !

kokjinsam commented 8 years ago

I'm not sure if I'm understanding the problem correctly. So you want to pass a variable from component to action?

You can do something like this:

// containers/componentA.js
...
export const depsMapper = (context, actions) => {
  const {
    someAction
  } = actions.actionsForA

  onChangeInput(someVar) {
    // do something with someVar
    someAction(someVar);
 }
  return {
    onChangeInput,
    context: () => context
  };
};
...

Then in your component, you could do something like this:

<Input
   name="name"
   label="Tên nhóm hàng"
   placeholder="Nhập tên nhóm hàng"
   value={name}
   onChange={this.props.onChangeInput.bind(this, {form: this})} />
lyquocnam commented 8 years ago

thanks for help @sammkj im not good english to say exactly :D you see we have to bind {form: this} to send all states from component to actions. so we have any other ways to get component state from actions without bind ? this code will clean :dancer:

kokjinsam commented 8 years ago

Then you will need to use React class and put those .bind stuffs in the constructor

lyquocnam commented 8 years ago

yeah ! so we can get state from context in actions ?

{
  _login({Meteor, **State**}, event) {
        // State is state in component.
       //
       // or use object **this**
      // 
         this.setState({name: '123'}); // this here the same with this in component.

   }
}

this option can be ? @sammkj :dancers:

kokjinsam commented 8 years ago

You should use LocalState instead. React state could be used if the state doesn't affect other components.