这是我的“提醒”应用。
我的问题是:我想点击编辑按钮来编辑项目并进行更新。每个项目都有一个随机ID,用于指定其操作。
过程如下:
我曾经在我的reducer中获取该提醒列表,然后在每次按下按钮“ X”时删除项目时在列表中使用make函数,然后使用.filter()。对于编辑功能,我使用filter( <在此处给一个ID === id>),但这已清除了列表,但被舔的除外!我尝试使用.find(),但这返回的对象不是数组:所以我在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" >✕</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);
// 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
}
}
// 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;
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')
);
答案 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)