React.js,Redux,制作提醒应用

时间:2018-06-27 14:56:22

标签: javascript reactjs

这是我的“提醒”应用。

我的问题是:我想点击编辑按钮来编辑项目并进行更新。每个项目都有一个随机ID,用于指定其操作。

过程如下:

  1. 动态显示“编辑”按钮,就像删除按钮一样
  2. 按下“编辑”按钮时,应在屏幕上的某个位置创建输入,包括提醒的文本值和名为“更新文本”的新按钮
  3. 在编辑值并单击“更新文本”按钮时,列表中的项目必须动态更改。

我曾经在我的reducer中获取该提醒列表,然后在每次按下按钮“ X”时删除项目时在列表中使用make函数,然后使用.filter()。对于编辑功能,我使用filter( <在此处给一个ID === id>),但这已清除了列表,但被舔的除外!我尝试使用.find(),但这返回的对象不是数组:所以我在App.jsx中遇到问题,因为我正在映射该数组。

我的App.jsx文件

import React, { Component } from 'react';
import "../App.css"
import { connect } from 'react-redux';
import { addReminder } from '../actions';
import { Button } from 'react-bootstrap';
import { deleteReminder } from '../actions'
import moment from 'moment'
import { modifyReminder } from '../actions'

class App extends Component {
// as usual we add our constructor to make states
constructor(props){
    super(props);

    // make states !
    // NOTE: the dueDate key was added recently
    this.state= {
        text: '',
        dueDate: '',
        alertDate: ''
    }

}

// Warning: this is not the imported addReminder from ../actions, its the helper function
addReminder(){
    // I hided this console.log after using it , to show state of our input component 
    //console.log('this.state ', this.state);

    //Note: after making the connect(), we check if our application is connected to the store or not
    // normally we'll find that props are containing our action creator (addReminder()) but without data
    // console.log('this ', this);

    // Now after checking the console, if our action creator is in the props of our App! 
    // so we can call it directly! (remember we have console.log() to show the action in actions folder)
    // Also we will check wheter the reducer is working or not, ( we also have console.log in the reducers folder)

    //NOTE: dueDate was added recently
    console.log('this.state.dueDate', this.state.dueDate)

    /** HERE WE ARE CALLING THE IMPORTED addReminder() from actions folder**/
    this.props.addReminder(this.state.text, this.state.dueDate);
}

// Warning: this is not the imported deleteReminder from ../actions, its the helper function
deleteReminder(id){

    console.log('deleting in application', id) // outputs the id of the reminder that we click to delete
    console.log('this.props', this.props) // this will prouve that our app is still connected with redux

    /** HERE WE ARE CALLING THE IMPORTED deleteReminder() from actions folder**/
    this.props.deleteReminder(id)
}

// Need to be handled
/*
modifyReminder(id){

    console.log('modifying in application', id)
    console.log('this.props', this.props)

    this.props.modifyReminder(id)

    const {reminders} = this.props

    return (
        reminders.map( reminder => {
        console.log('text is ' , reminder.text)

            return(     
                        <TextModified textModifier={reminder.text} />
                    )
        })  
    )   

}
*/
// After making connection between redux and the app,
// After making reactivity between UI, User and states
// and then we got our data from states.
// Now the user doesn't know that yet, so we have to show the list of reminders entered by the user
renderReminders() {

    // here we declare a const variable, it will contain reminders list from mapStateToProps()
    const {reminders} = this.props;     
    // outputs reminders [id:, text:''] as list
    console.log('Reminders as list', reminders)

    // return jsx 
    return (
            // <ul> html tag for lists
            <ul className="list-group col-sm-4">
                {
                    // we map our list, getting a value by a key
                    reminders.map( ( reminder ) => {    
                        // list item                    
                        return (
                                    <li key = { reminder.id } className='list-group-item'>
                                        {/*the list item will have text and a date*/}
                                        <div className="list-item">
                                            {/*show the reminder's text*/}
                                            <div>{reminder.text}</div>
                                            {/*show the reminder's date, <em> jsx tag is used to emphasize*/}
                                            {/*install (moment --save) through yarn to format the date and time*/}
                                            <div>
                                                <em>
                                                    {moment(new Date(reminder.dueDate)).fromNow()}
                                                {/* this.setState({alertDate: reminder.dueDate})*/}
                                                </em>
                                            </div>
                                        </div>
                                    {/* We add a button here to delete a reminder from the list
                                    1 to create a deletion of an item we must have a logic,
                                    2 go to constants.js and make a new const */}

                                        <div 
                                            className="list-item delete-button"
                                            onClick= {() => this.deleteReminder(reminder.id)}>
                                            {/*THIS IS THE REMINDER DELETION*/}
                                            <div className="btn delete-item" >&#x2715;</div>
                                        </div>

                                        {/*<div className="Modify-item" 
                                        onClick = {() => this.modifyReminder(reminder.id)}>
                                            Modify
                                        </div>
                                        */}

                                    </li>
                                )
                        })
                }
            </ul>
            )       
}

render(){
    console.log('this.props ', this.props); // this will show props every time render() is called

    //const lastName = 'Firas';
    //console.log(`Hello mr.${lastName}`)
    return(
            <div className="App">
                {/* this is ou title */}
                <div className="Title">
                    My Reminder 
                </div>
                <div className="form-inline">

                    <div className="form-group">

                        {/*this is the reminder tag*/}
                        <input className="form-contol"
                                placeholder="I have to..."
                                onChange={event => this.setState({text: event.target.value})}/>

                        {/*this is the date tag*/}
                        <input className="form-contol" type="datetime-local"
                        onChange={ event => this.setState({dueDate: event.target.value})}/>
                    </div>
                    {/* this is the button */}
                    <Button  type="button" 
                            className="btn btn-success" 
                            onClick= {() => this.addReminder()}>
                        Add reminder
                    </Button>

                </div>
                {/*THIS IS THE REMINDERS LIST, IT WIL BE SHOWN WHEN WE ADD REMINDERs
                THIS function will be called everytime render() is called */}
                {this.renderReminders()}
                <div className="btn bnt-danger" onClick={() => this.props.clearReminders()}>
                    Clear Reminders
                </div>                                          
            </div>
            )
    }
}
/** TODO: After making the view, and setting up redux search for connect(mapDispatchToProps function, mapStateToProps function) **/

// this function is hooked by connect(), it dispatch the action, it returns a bindActionCreators
// function, that turns an object 

// I hided this function since, I can pass an object instead of the entire function
// so by using addReminder imported from actions folder, mapDispatchToProps is called
// automaticaly and use this object in its bindActionCreators()
/*
function mapDispatchToProps(dispatch){
    return bindActionCreators({addReminder}, dispatch);
}
*/

// We can define states to props, so we can recognize the redux state within this component
// This function will be passed as first arg in connect(), and it will be hoocked by it,
// just like mapDispatchToProps function
function mapStateToProps(state){
    // I hided this and replace it under render() method
    //console.log('state ', state);
    return {
        reminders: state
    }
}

// now we connect it to our component, by connect() from redux
// the first argument should be mapStateToProps(), but we don't have it yet, so we pass it as null
// the second argument is mapDispatchToProps()
// and then we'll have our App component hooked up

// NOTE: deleteReminder was added here after defining it in the actions, after the addReminder()
export default connect(mapStateToProps , {addReminder, deleteReminder, modifyReminder, clearReminders}) (App);

我的动作/index.js

// Welcome to actions/index.js file !
// First we need to get the action from ../constants.js
import { ADD_REMINDER} from '../constants';

// NOTE: this is related to deletion of list item, its not related to redux setup
// we imported our type of action (delete) from ../constants.js file
import { DELETE_REMINDER } from '../constants';

import { MODIFY_REMINDER } from '../constants';
import { CLEAR_REMINDERS } from '../constants'
/**
* This is our action creator, it's called addReminder,
* its assigned by an ANONYMOUS ARROW function that will have - in our case - 
* 1 parameter, its a text that we'll pass to our addReminder action creator
*/
export const addReminder = ( text , dueDate ) => { // it should be written as (text) if it has more than one arg

    // here we define the Action ( Plain JS object)
    const action = {
        type: ADD_REMINDER, // this type name was imported from the constants.js file

        // we can use ES6 syntax feature if the key and value are same (text.equals(text))
        // so just write text.
        // or text: text ( both are correct)
        text: text,
        dueDate: dueDate,
    }

    // Log the action through the action creator into the console, this will show, later,
    // whether our action is connected to the application or not
    console.log('Action in addRemider :',action );

    // return the action
    return action;
}

// NOTE: this is related to redux setup:
// Now we go to App.jsx file, and after running the code, an error occurs telling that
// "Expected the reducer to be a function", so let's go to create reducers/index.js file


// NOTE: the next step is considered after the step in which we show the reminders list.

/**
* TODO: make a logic to delete a list item
* -> we need to identify the item to delete it, luckily we created an id for each item so we can specify them
* so our argument in this function will  be an id
*/
export const deleteReminder = id => {

    const action = {
        type: DELETE_REMINDER,
        id:id // id here is the arg in our function deleteReminder()
    }

    console.log('Deleting in actions', action)

    return action
}

export const modifyReminder = id => {

    const action = {
        type: MODIFY_REMINDER,
        id
    }

    console.log('Modifying in actions', action)

    return action
}

export const clearReminders = () => {
    return {
        type: CLEAR_REMINDERS
    }
}

我的reducers / index.js文件

// Welcome to reducers/index.js file
// First, as we saw in actions/index.js, we need to import the type from constants file 
import { ADD_REMINDER/*, ADD_FAVORITE*/ , DELETE_REMINDER , MODIFY_REMINDER , CLEAR_REMINDERS} from '../constants';

import { bake_cookie, read_cookie } from 'sfcookies' // cookies are used to save some data locally

/**
* Step 2 define a helper reminder() function which takes 1 arg ( action ) 
*/
const reminder = action => {

    let { text,dueDate } = action;
    // we return an object as a reminder, with a text and a random ID ( for example )
    return {
        id: Math.random(), // check Math JS class on google for better information
        text: text,
        dueDate: dueDate,
    }
}

/** Step 3 removeById function
* we'll have an arrow function that will have 2 args: 
* 1 - state = [] -> the list of our reminders
* 2 - id -> the id of the concerned item to delete
* the function is going to filter the list
* then only returns the IDs that are not equal to the clicked list item id
*/
const removeById = (state = [], id) => {

    const reminders = state.filter( reminder => reminder.id !== id)

    // this will show the filtered list
    console.log ('new reduced reminders', reminders)

    return reminders
}

const modifyById = (state = [], id) => {

    const reminders = state.find( (reminder) => reminder.id === id )

    console.log ('new reduced reminders', reminders)

    return reminders
}

/** Step 1 Reducer creation: it will be an ANONYMOUS ARROW function,
 * it has 2 parameters: 
 * ( state[] - preinitialized to an empty array -  , - and a second arg which is - action ) 
 * -> (state[],action)
 */
 const reminders = (state = [], action) => { // it can be written as state = [] without if parentheses if there is only arg

    // We initialize a variable here within our reminders reducer to null 
    let reminders = null;

    state = read_cookie('reminders')

    // Generally we can expect more than one type of action entered here in the future, 
    // besides addReminder() (the action creator)
    // so let's use a switch statement
    switch(action.type) {

        // we consider our first case as our ADD_REMINDER defined in constants.js file
        case ADD_REMINDER: 

            // -> in this case we set our reminders to an array 
            // -> Here we are using a NEAT ES6 trick
            // Our first element will be a spread object ( like varargs in Java ) which was our state array
            // Our second element will be a reminder() that will take an action parameter, check Step 2
            reminders = [...state, reminder(action)];

            // Log the reminder as state through the reducer into the console, this will show if our reducer is connected 
            // to the application or not
            //console.log('reminders as state', reminders);

            // save cookie to our browser
            bake_cookie('reminders', reminders)

            // we return the reminders tht we've gotten 
            return reminders;

        // we consider our second case as our DELETE_REMINDER defined in constants.js file
        case DELETE_REMINDER:
            // in this, we move to declare our removeId function first 
            reminders = removeById(state, action.id)
            bake_cookie('reminders', reminders)
            return reminders

        case MODIFY_REMINDER:
            reminders = modifyById(state, action.id)
            return reminders

        case CLEAR_REMINDERS:
            reminders = []
            bake_cookie('reminders', reminders)
            return reminders;

        // otherwise, if we got other types than "ADD_REMINDER"
        default:
            return state;   
    }
}

export default reminders;

我的src / index.js文件

import React from 'react';
import ReactDOM from 'react-dom'; //ReactDOm 
import App from './components/App'; // this is ur App
import { Provider } from 'react-redux';// Provider will make the applicaton under the store
import { createStore } from 'redux'; // a store is a Provider prop
import reducer from './reducers';
import './index.css'; // notice how we import css file

// NOTE: When we import the Provider and place our App into it, the console will 
// throw an error asking to define a store, a store prop belongs to Provider, so we need to createStore() 
// to create the store, and later we'll pass a param to this function, that takes states in an
// an action, and return new state
//const store = createStore(); // this line was added without reducers at the beginning
const store = createStore(reducer);

ReactDOM.render(
    /* 
      A store that stores all the data and provides methods to manipulate this data.
      The store is created with the createStore() function
    */
    /*
      A Provider component that makes it possible for any components to take data
      from the store
    */
    <Provider store={store}> 
        <App />
    </Provider>,
    document.getElementById('root')
    );

这是我的应用的GIF enter image description here

1 个答案:

答案 0 :(得分:-1)

在减少字段的当前状态时,Redux的处理有些棘手。

要删除列表中的项目:(省略来自lodash

case DELETE_ITEM: {
  return omitBy(state, item => item.id === action.id);
  // OR in vanilla 
  const itemIndex = list.findIndex(item => item.id === action.id);
  const newState = [...state];
  newState.splice(itemIndex, 1)
  return newState;
}

要编辑列表中的项目:

case MODIFY_ITEM: {
  const itemIndex = list.findIndex(item => item.id === action.id);
  return {
    ...state,
    [itemIndex]: { 
      ...state[itemIndex], 
      ...action.newItem // newItem is an object containing the properties you want to modify
    }
  }
}

这是一个有点伤感使用的功能时,您的列表是充满的ID进行循环如findIndex或省略。您可以使用keyBy进一步改进此代码。 (您也可以在普通ES6中执行此操作),甚至可以使用Immutable.js将数组转换为以id为键的Maps,然后可以访问mapOfItems[itemId]这样的项目。

状态为地图时,编辑用例将如下所示:

case MODIFY_ITEM: {
  return {
    ...state,
    [action.id]: { 
      ...state[action.id], 
      ...action.newItem,
    }
  }
}

删除操作就像omit(state, action.id)