我正在处理小型拖放应用。
我希望能够在页面刷新后坚持它的状态,所以我想到了使用Redux并坚持选择的键。
我使用React-dnd并且已经存在拖放逻辑。在某些事件(例如丢弃)上,我添加了要调度的操作。这些操作会更改redux状态并将所选项目从一个列表移动到另一个列表。我可以看到redux商店中的变化,但它们不会出现在用户界面中。即使Redux状态发生变化,道具基本上也不会改变。在使用相同选择器的同时,我收到了UI的正确初始状态。
我的问题:道具不会更新状态变化。
我的问题:为什么以及我做错了什么?
MaveISeenItContainer.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import HTML5Backend from 'react-dnd-html5-backend';
import {DragDropContext} from 'react-dnd';
import * as actions from '../../actions/MoviesActions';
import HaveISeenItComponent from './HaveISeenItComponent';
import {getSelectedMovies, getAllMovies, getStatus} from '../../reducers/MoviesReducer';
let HaveISeenItContainer = props => <HaveISeenItComponent {...props} />;
const mapDispatchToProps = dispatch => ({
getMovies: () => dispatch(actions.getMovies.request()),
selectMovie: values => dispatch(actions.selectMovie.request(values)),
removeMovie: values => dispatch(actions.removeMovie.request(values))
});
const mapStateToProps = state => ({
allMovies: getAllMovies(state),
selectedMovies: getSelectedMovies(state),
status: getStatus(state)
});
HaveISeenItContainer = DragDropContext(HTML5Backend)(HaveISeenItContainer);
export default connect(mapStateToProps, mapDispatchToProps)(HaveISeenItContainer);
HaveISeenItComponent.js
class HaveISeenItComponent extends Component {
componentDidMount(id, index) {
if (this.props.status !== 'SUCCESS') {
this.props.getMovies();
}
}
handleMovieSelect = (id, card) => {
this.props.selectMovie({listId: id, id: card.id, card: card})
};
handleMovieRemove = (id, index) => {
this.props.removeMovie({listId: id, cardId: index});
};
render() {
const {selectedMovies, allMovies} = this.props;
let availableMoviesList = null;
let moviesToBeSeenList = null;
if (this.props.status === 'SUCCESS') {
const allMoviesList = _.map(allMovies, (value, key) => ({
id: key,
title: value.title
})
);
const selectedMoviesList = _.map(selectedMovies, (value, key) => ({
id: key,
title: value.title
})
);
availableMoviesList = <List
id={1}
list={allMoviesList}
onMovieRemove={this.handleMovieRemove}
onMovieSelect={this.handleMovieSelect}
/>;
moviesToBeSeenList = <List
id={2}
list={selectedMoviesList}
onMovieRemove={this.handleMovieRemove}
onMovieSelect={this.handleMovieSelect}
/>;
}
return (
<div className={styles.mainComponent}>
{moviesToBeSeenList}
{availableMoviesList}
</div>
);
}
}
HaveISeenItComponent.propTypes = {
getMovies: PropTypes.func
};
export default HaveISeenItComponent;
List.js
class List extends Component {
constructor(props) {
super(props);
this.state = {cards: props.list};
}
pushCard(card) {
const {onMovieSelect, id} = this.props;
onMovieSelect(id, card);
}
removeCard(index) {
const {onMovieRemove, id} = this.props;
onMovieRemove(id, index);
}
moveCard(dragIndex, hoverIndex) {
const {cards} = this.state;
const dragCard = cards[dragIndex];
this.setState(update(this.state, {
cards: {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragCard]
]
}
}));
}
render() {
const {cards} = this.state;
const {canDrop, isOver, connectDropTarget} = this.props;
const isActive = canDrop && isOver;
const style = {
width: "200px",
height: "404px",
border: '1px dashed gray'
};
const backgroundColor = isActive ? 'lightgreen' : '#FFF';
return connectDropTarget(
<div style={{...style, backgroundColor}}>
{cards.map((card, i) => {
return (
<Card
key={card.id}
index={i}
listId={this.props.id}
card={card}
removeCard={this.removeCard.bind(this)}
moveCard={this.moveCard.bind(this)}/>
);
})}
</div>
);
}
}
const cardTarget = {
drop(props, monitor, component) {
const {id} = props;
const sourceObj = monitor.getItem();
if (id !== sourceObj.listId) component.pushCard(sourceObj.card);
return {
listId: id
};
}
}
export default DropTarget("Card", cardTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop()
}))(List);
MoviesReducer.js(带基本选择器)
import _ from "lodash";
// import * as actions from 'actions/MoviesActions';
// import { REQUEST_STATUS } from "constants/types";
import * as actions from '../actions/MoviesActions';
import {REQUEST_STATUS} from '../constants/types';
import {LIST_INDEX} from '../constants/constants';
const initialState = {
allMovies: [],
selectedMovies: []
};
const MoviesReducer = (state = initialState, action) => {
switch(action.type){
case actions.GET_MOVIES.REQUEST:
return {
...state,
status: REQUEST_STATUS.PENDING
};
case actions.GET_MOVIES.SUCCESS:
return {
...state,
status: REQUEST_STATUS.SUCCESS,
allMovies: action.response.movies
};
case actions.GET_MOVIES.FAILURE:
return {
...state,
status: REQUEST_STATUS.FAILURE
};
case actions.SELECT_MOVIE.REQUEST:
if(action.values.listId === LIST_INDEX.SELECTED_MOVIES){
state.selectedMovies.splice(action.values.cardId, 0, action.values.card);
} else if (action.values.listId === LIST_INDEX.ALL_MOVIES){
state.allMovies.splice(action.values.cardId, 0, action.values.card);
}
return state;
case actions.REMOVE_MOVIE.REQUEST:
if(action.values.listId === LIST_INDEX.SELECTED_MOVIES){
state.selectedMovies.splice(action.values.cardId, 1);
} else if (action.values.listId === LIST_INDEX.ALL_MOVIES){
state.allMovies.splice(action.values.cardId, 1);
}
return state;
case actions.MOVE_MOVIE.REQUEST:
return state;
default:
return state;
}
};
export const MOVIES_STATE_KEY = 'moviesState';
// Selectors
export const getStatus = state => _.get(state, [MOVIES_STATE_KEY, 'status']);
export const getAllMovies = state => _.get(state, [MOVIES_STATE_KEY, 'allMovies']);
export const getSelectedMovies = state => _.get(state, [MOVIES_STATE_KEY, 'selectedMovies']);
export default MoviesReducer;