Heathercoraje / pomotris-vh

Pomodoro time tracker with colorful records, trust me. you'd love it :heart: Built with React
0 stars 1 forks source link

Code Style Refactors #39

Closed iwilsonq closed 6 years ago

iwilsonq commented 6 years ago

One more thing! Most of the things I'd improve are things that your linter actually pick up, just be sure to get eslint working with your IDE 👍

For example, using destructuring in Timers.jsx:

handleRecordSubmit = () => {
  const { category, title, startTime, duration, onRecordSubmit } = this.state;
  const id = uuid();
  onRecordSubmit({ category, title, startTime, duration, id });
};

Regarding the assignment of const id = uuid() here, this operation is usually done on the backend, sometimes even by the database you're using. In the spirit of keeping backend concerns separate from the client, I'd move this to your apiLocalStorage service that 'mocks' the backend, namely to the saveRecords method.

For now, saving the whole list of records works with smaller data, but I'd recommend creating a saveOne or insertOne method on the 'backend' service and just call that method multiple times. This'll make it easier to compose API calls on the backend; remember that each record might have dozens of fields, it would be quite a lot of work to save multiple records at a time. On top of that there might be a pipeline of 6 methods or hooks that need to be called on the backend for each insertOne call in order to keep the integrity all of the other data in order.

That tangent aside, I think for the scope of this project, you've implemented it pretty well 😄

Inside CategorySetting.jsx there are some concerns with naming. The Field component should be called CategoryColorForm or even CategoryColorGroup. For the fields field in CategorySetting state, I think that fields should be called categories and category should be renamed to name, since it feels like from an outsiders perspective that fields is pretty broad and can be interpreted for many different parts of this app.

To summarize that, ive updated the code of CategorySetting's render method

    render() {
        return (
            <div className="setting">
                <hr />
                <p className='setting-title'>Category Setting</p>
                                <form onSubmit={this.onSave}>
                                 {this.state.categories.map((category, i) => (
                       <CategoryColorGroup key={i} id={i} 
                                     data={category}
                                     onInputChange={this.onInputChange} 
                                   />
                         ));}
                               </form>

                <button className="button-modal-save" onClick={this.onSave}>
                    Save
                </button>
                <button className="button-modal-cancel" onClick={this.onCancelClick}>
                    Cancel
                </button>
            </div>
        );
    }

By wrapping all of the category color groups in a form and giving that form an onSubmit prop, the user can submit it by pressing enter. They can also click the save button as it stands. The big thing to note is that there is only one form component and n inputs, one for each category. I think this makes sense with the current setup, I wouldn't expect n forms within the same modal after all.

I'll stop for now, comment if you have any questions 🔨

Heathercoraje commented 6 years ago
handleRecordSubmit = () => {
  const { category, title, startTime, duration, onRecordSubmit } = this.state;
  const id = uuid();
  onRecordSubmit({ category, title, startTime, duration, id });
};

@iwilsonq But onRecordSubmit is a function, a prop from a parent component. Do you mean to suggest adding onRecordSubmit in state of this component? and just use variable?

iwilsonq commented 6 years ago

Oh I see my error, I meant to take onRecordSubmit from props! I intended to say that there are places where es6 destructuring can make it cleaner and easier to read 😅 . Compare that snippet to this one

handleRecordSubmit = () => {
  const category = this.state.category;
  const task = this.state.task;
  const startTime = this.state.startTime;
  const duration = this.state.duration;
  const id = uuid();
  this.props.onRecordSubmit({ category, task, startTime, duration, id });
};
Heathercoraje commented 6 years ago

because jsx component has be inside a enclosing tag, so I am adding

as wrapper

class CategoryColorGroup extends Component {
    render() {
        const ID = this.props.id;
        const category = this.props.data.category;
        const color = this.props.data.color;
        return (
            <div className="category-color-form">
                <input
                    id={ID}
                    name="color"
                    type="color"
                    value={color}
                    className="input-color"
                    onChange={this.props.onInputChange}
                />
                <input
                    id={ID}
                    name="category"
                    value={category}
                    className="input-category"
                    placeholder="new category"
                    onChange={this.props.onInputChange}
                    autoComplete="off"
                />
            </div>
        );
    }
}

Then, though I have only one form and also onSubmit fn() attached, when I press enter on CategoryColorGroup, function does not get invoked. Is it because I have div as wrapper?

    render() {
        return (
            <div className="setting">
                <hr />
                <p className="setting-title">Category Setting</p>
                <form onSubmit={this.onSaveClick}>
                    {this.state.categories.map((category, i) => (
                        <CategoryColorGroup
                        key={i}
                        id={i}
                        data={category}
                        onInputChange={this.onInputChange}
                        />
                    ))}
                </form>
                <button className="button-modal-save" onClick={this.onSaveClick}>
                    Save
                </button>
                <button className="button-modal-cancel" onClick={this.onCancelClick}>
                    Cancel
                </button>
            </div>
        );
    }
}
Heathercoraje commented 6 years ago

@iwilsonq also, did you mean to say to destruct ones that are not functions as such below?

handleRecordSubmit = () => {
        const { category, task, startTime, duration, onRecordSubmit} = this.state;
        const color = generateRandomColor(category, this.props.categories);
        const id = generateID();
        this.props.onRecordSubmit({ category, task, startTime, duration, color, id });
    };
iwilsonq commented 6 years ago

Yes like that basically, or this

handleRecordSubmit = () => {
  const { category, task, startTime, duration} = this.state;
  const color = generateRandomColor(category, this.props.categories);
  const id = generateID();
  this.props.onRecordSubmit({ category, task, startTime, duration, color, id });
};
iwilsonq commented 6 years ago

As for the form, it should work I think, can you use console.log and see if this.onSave is being called?

Heathercoraje commented 6 years ago

if you refer to this.onSaveClick it does not get invoked.

iwilsonq commented 6 years ago

Well looking back, the categories probably dont need to be rendered from state, you should try to keep the state of your component as light as possible. There are two ways to do this here:

  1. Controlled component form input. You'll use component state in CategorySettings to store the currently entered values of the inputs. When onSubmit is called from the form, the onSave function takes those values out of state and submits them.

  2. Regular HTML form. You'll just have a bunch of tags under your form, when you hit submit, your form will have a reference to those inputs. This is more fragile/static since it doesnt rely on component state.

Basically, use state to hold the values you care about. Don't use state to render input fields at all, unless its like a showField boolean.

Heathercoraje commented 6 years ago

If I understood correctly, the first option is basically what the current codes are doing. onInputChange controls the input as it changes and stored them as state. then OnSaveClick, it submits category data inside state.

Heathercoraje commented 6 years ago

also still not understanding why it does not log out anything.

onSaveClick = () => {
  console.log('hello world')
  const data = this.state.categories;
  this.props.onSave(data);
  this.props.closeModal();
};

onCancelClick = () => {
  this.props.closeModal();
};
render() {
  return (
    <div className="setting">
      <hr />
      <p className="setting-title">Category Setting</p>
      <form onSubmit={this.onSaveClick}>
      {this.state.categories.map((category, i) => (
      <CategoryColorGroup
         key={i}
         id={i}
         data={category}
         onInputChange={this.onInputChange}
       />
      ))}
       </form>
       <button className="button-modal-save" onClick={this.onSaveClick}>
          Save
       </button>
       <button className="button-modal-cancel" onClick={this.onCancelClick}>
      Cancel
       </button>
     </div>
    ); 
  }
}