Redux with Immer not updating component - reactjs

I'm trying to add an element to an array in an object in my Redux store. I see the object get added to the store but it is not updating the component. If I leave the page and return it is showing up.
I'm pretty sure this is a state mutation issue but I can't figure out where I'm going wrong unless I fundamentally misunderstand what Immer is doing. In the component I'm using produce to add the string to the array, passing the new object to my reducer and using produce to add that object to an array of those objects.
I've looked through a ton of similar questions that all relate to state mutation, but the way I understand it the return from the component's call to produce should be a fully new object. Then in the reducer the call to produce should be returning a new object array.
This is the first time using Immer in a large project so it's entirely possible I don't fully get how it's working it's magic.
Component
import produce from 'immer';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import uuid from 'uuid/v4';
import { generate } from 'generate-password';
import { updateLeague } from '../../actions/leagues';
import { addTeam } from '../../actions/teams';
import { addUser } from '../../actions/users';
import Team from '../../classes/Team';
import User from '../../classes/User';
import UserWidget from '../utils/user/UserWidget';
class ViewLeague extends Component {
state = {
league : null,
isOwner : false,
owner : '',
teams : [],
inviteEmail: ''
};
componentWillMount() {
console.log('mount props', this.props.leagues);
const { leagues, uuid, leagueId, users, teams } = this.props;
if (leagues.length > 0) {
const league = leagues.find(league => league.uuid === leagueId);
const owner = users.find(user => league.leagueManager === user.uuid);
const leagueTeams = teams.filter(team => league.teams.includes(team.uuid));
this.setState({
league,
isOwner: league.leagueManager === uuid,
owner,
teams : leagueTeams
});
}
}
handleUpdate(event, fieldName) {
this.setState({ [ fieldName ]: event.target.value });
}
findUserByEmail(email) {
//Todo if not found here hit server
return this.props.users.find(user => user.email === email);
}
sendInvite = () => {
const { addTeam, addUser, updateLeague } = this.props;
const { league } = this.state;
const newManager = this.findUserByEmail(this.state.inviteEmail);
const newTeamUuid = uuid();
let newLeague = {};
if (newManager) {
const newTeam = new Team('New Team', newManager.uuid, newTeamUuid);
addTeam(newTeam);
} else {
const newPass = generate({
length : 10,
number : true,
uppercase: true,
strict : true
});
const newUserUuid = uuid();
const newUser = new User('', this.state.inviteEmail, newPass, '', '', newUserUuid);
addUser(newUser);
const newTeam = new Team('New Team', newUserUuid, newTeamUuid);
addTeam(newTeam);
newLeague = produce(league, draft => {draft.teams.push(newTeamUuid);});
updateLeague(newLeague);
console.log('invite props', this.props);
console.log('league same', league === newLeague);
}
//Todo handle sending email invite send password and link to new team
console.log('Invite a friend', this.state.inviteEmail);
};
renderInvite() {
const { isOwner, league, teams } = this.state;
if (isOwner) {
if ((league.leagueType === 'draft' && teams.length < 8) || league.leagueType !== 'draft') {
return (
<div>
<p>You have an empty team slot. Invite a fried to join!</p>
<input type="text"
placeholder={'email'}
onChange={() => this.handleUpdate(event, 'inviteEmail')}/>
<button onClick={this.sendInvite}>Invite</button>
</div>
);
}
}
}
renderViewLeague() {
console.log('render props', this.props.leagues);
const { league, owner, teams } = this.state;
const editLink = this.state.isOwner ?
<Link to={`/leagues/edit/${this.props.leagueId}`}>Edit</Link> :
'';
return (
<div>
<h2>{league.leagueName} </h2>
<h3>League Manager: <UserWidget user={owner}/> - {editLink}</h3>
<p>League Type: {league.leagueType}</p>
{this.renderInvite()}
<br/>
<hr/>
<h2>Teams</h2>
<span>{teams.map((team) => (<p key={team.uuid}>{team.teamName}</p>))}</span>
<span>
<h2>Scoring: </h2>
{league.scoring.map((score, index) => (
<p key={index}>{`Round ${index + 1}: ${score} points`}</p>
)
)}
</span>
</div>
);
}
render() {
if (!this.state.league) {
return (
<div>
<h2>No league Found</h2>
</div>
);
} else {
return (
<div>
{this.renderViewLeague()}
</div>
);
}
}
}
export default connect(
({ leagues: { leagues }, teams: { teams }, users: { users }, auth: { uuid } },
{ match: { params: { leagueId } } }) => ({
leagues,
teams,
users,
uuid,
leagueId
}), ({
addTeam : (team) => addTeam(team),
addUser : (user) => addUser(user),
updateLeague: (league) => updateLeague(league)
})
)(ViewLeague);
Reducer
import produce from 'immer';
import {
ADD_LEAGUE,
UPDATE_LEAGUE
} from '../actions/types';
const DEFAULT_LEAGUES = {
leagues: [ {
leagueName : 'Test League',
leagueManager: 'testUser12345',
uuid : 'testLeague12345',
teams : [ 'testTeam12345', 'testTeam23456' ],
scoring : [ 25, 20, 15, 10, 5, -5 ],
leagueType : 'draft'
} ]
};
const leaguesReducer = (state = DEFAULT_LEAGUES, action) =>
produce(state, draft => {
// noinspection FallThroughInSwitchStatementJS
switch (action.type) {
case ADD_LEAGUE:
draft.leagues.push(action.league);
case UPDATE_LEAGUE:
console.log('updating league', action.league);
const { league } = action;
const leagueIndex = draft.leagues.findIndex(fLeague => league.uuid === fLeague.uuid);
draft.leagues.splice(leagueIndex, 1, league);
}
});
export default leaguesReducer;
Any help is greatly appreciated!! More info available if needed

Try adding return; at the end of your case blocks.
You can read more about returning data from producers and see examples of what to do and what not to do here.

Related

React - Render Key Press Event

I cannot seem to find a fitting example anywhere online. I have little experience with javaScript and React, and my issue might be trivial. The keypress event function works fine if run it by itself. However, if I try to implement it into the class app, and call the function from the render section I get this error: Error message. Any ideas? Thanks in advance. I have added the code.
import React, { Component, useEffect, useState } from 'react';
import './App.css';
import Spotify from 'spotify-web-api-js';
const spotifyWebApi = new Spotify();
class App extends Component {
constructor(){
super();
const params = this.getHashParams();
this.state = {
loggedIn: params.access_token ? true : false,
nowPlaying: {
name: 'Not Checked',
image: '',
device: '',
user_id: '',
playlists: []
}
}
if (params.access_token){
spotifyWebApi.setAccessToken(params.access_token)
}
};
useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState();
// Når du trykker på knappen - sætter vi keyPressed til true for at vise resultatet.
function downHandler({ key }) {
if (key === targetKey) {
this.setKeyPressed(true);
}
}
// Når du releaser knappen - sætter vi keyPressed til false for at fjerne resultatet igen.
const upHandler = ({ key }) => {
if (key === targetKey) {
this.setKeyPressed(false);
}
};
useEffect(() => {
window.addEventListener('keydown', downHandler);
window.addEventListener('keyup', upHandler);
// Det er altid "pænt" at ryde op efter sig selv, så vi fjerner eventListeners i return metoden
return () => {
window.removeEventListener('keydown', downHandler);
window.removeEventListener('keyup', upHandler);
};
}, []);
return this.keyPressed;
}
Fapp() {
const aPressed = this.useKeyPress('a');
const sPressed = this.useKeyPress('s');
const dPressed = this.useKeyPress('d');
const fPressed = this.useKeyPress('f');
return (
<div>
{ aPressed ? 'a' : 'not a'}
</div>
);
}
getHashParams() {
var hashParams = {};
var e, r = /([^&;=]+)=?([^&;]*)/g,
q = window.location.hash.substring(1);
while ( e = r.exec(q)) {
hashParams[e[1]] = decodeURIComponent(e[2]);
}
return hashParams;
}
getNowPlaying(){
spotifyWebApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response.item.name,
image: response.item.album.images[0].url
}
}
)
})
}
handleKeyDown(event) {
if(event.keyCode === 13) {
console.log('Enter key pressed')
}
}
render() {
return (
<div className="App">
<a href='http://localhost:8888'>
<button>Login with Spotify</button>
</a>
<div> Now Playing: { this.state.nowPlaying.name } </div>
<div> user: { this.state.nowPlaying.user_id } </div>
<div>
<img src={ this.state.nowPlaying.image } style={{width: 100}}/>
</div>
<button onClick={() => this.getNowPlaying()}>
Check Now Playing
</button>
</div>
);
};
}
export default App;

How to clear the FullCalendar cache events

After drop an event from an external list and save this event on me server side i keep seeing the dropped event which is not in my source array.
I tried a lot of solutions to clear events using EventSources as in this topic, but what is happening is that all the events who came from the API are cleared but not the one who I dropped the blue one (CACHED).
In action : I have already red event in my view, and I add another red event from my drop list, but after the drop you will see the new red event that I add + another blue event (CACHED EVENT) this blue event is not in my source event list.
Using calendar.getEventSources() to clear the event is clearning only the event from the API, results is like this
Any suggestions?
Thanks in advance
CODE :
import React, { Component } from "react";
import $ from 'jquery';
import moment from 'moment';
import { Trans } from 'react-i18next';
import { DebounceInput } from 'react-debounce-input';
import FullCalendar from '#fullcalendar/react';
import dayGridPlugin from "#fullcalendar/daygrid";
import timeGridPlugin from "#fullcalendar/timegrid";
import listPlugin from "#fullcalendar/list";
import interactionPlugin, { Draggable } from "#fullcalendar/interaction";
import { Module } from '../Calender/Module';
import { CalendarLoad } from '../../../../../Util/Svg';
import { getFlyerById } from '../../../../../Util/Api/Cms';
import { withAlert } from 'react-alert';
import "#fullcalendar/core/main.css";
import "#fullcalendar/daygrid/main.css";
import "#fullcalendar/timegrid/main.css";
import "#fullcalendar/list/main.css";
import './CalendarV4.css';
class CalendarDemo extends Component
{
calendarComponentRef = React.createRef()
state = {
modalData: null,
isModalOpen: false,
eventReceiveStartDate: null,
isEventinTheView: null,
isEventinTheViewValue: ''
};
componentDidMount()
{
const draggableEl = document.getElementById("externalEvents");
new Draggable(draggableEl, {
itemSelector: ".draggable-events",
eventData: function(eventEl)
{
const title = eventEl.getAttribute("title");
const id = eventEl.getAttribute("id");
const duration = eventEl.getAttribute("data-duration");
return {
title: title,
id: id,
duration: duration
};
}
});
}
eventDrop = info =>
{
const string = info.event.title;
const stringId = string.split(' - ')[0];
const id = parseInt(stringId)
this.props.schedulesEvent(id, info.event.start, info.event.end)
};
eventReceive = info =>
{
this.setState({ eventReceiveStartDate : moment(info.event.start).format("YYYY-MM-DD") })
this.props.addToCalendarHandler(info.event.id, info.event.start , info.event.end);
};
drop = info =>
{
if ($('#RemoveAfterDrop').is(':checked')) {
$(`#externalEvents #${info.draggedEl.id}`).each(function() {
$(this).remove();
})
}
};
eventClick = info =>
{
const string = info.event.title;
const stringId = string.split(' - ')[0];
const id = parseInt(stringId)
this.setState({ isModalOpen: true });
getFlyerById(info.event.id).then( res => {
this.setState({ modalData: {...res.data ,eventId: id} });
})
};
eventResize = info =>
{
const string = info.event.title;
const stringId = string.split(' - ')[0];
const id = parseInt(stringId)
this.props.schedulesEvent(id, info.event.start, info.event.end)
};
datesRender = info =>
{
const viewActiveStart = moment(info.view.activeStart).format("YYYY-MM-DD");
const viewActiveEnd = moment(info.view.activeEnd).format("YYYY-MM-DD");
const range = `${viewActiveStart}/${viewActiveEnd}`
this.props.datesRangeHandler(range);
}
deleterFlyer = id =>
{
this.setState({
modalData: null,
isModalOpen: false
});
this.props.deletingEventHandler(id)
}
modalToggleHandler = () =>
{
this.setState({ isModalOpen : false, modalData: null})
};
isEventinTheView = id =>
{
const calendar = this.calendarComponentRef.current.getApi();
this.setState({
isEventinTheView : calendar.getEventById( id ) === null ? false : true,
isEventinTheViewValue: calendar.getEventById( id ) === null ? 'No' : 'YES'
})
if ( id === '' ) {
this.setState({ isEventinTheViewValue : '' })
}
};
testingAndDebugging = () => {
const calendar = this.calendarComponentRef.current.getApi();
let eventSources = calendar.getEventSources();
for (var i = 0; i < eventSources.length; i++) {
eventSources[i].remove();
}
};
render()
{
const { modalData, isModalOpen, isEventinTheViewValue, isEventinTheView } = this.state;
const { modifiedEvents } = this.props
return (
<div>
<FullCalendar
plugins={[
dayGridPlugin,
timeGridPlugin,
listPlugin,
interactionPlugin
]}
header= {[{
center: "title",
right : "today prev,next",
}]}
firstDay ={1}
timezone ="UTC"
defaultView ="dayGridMonth"
editable ={true}
droppable ={true}
selectable ={true}
eventLimit ={true}
forceEventDuration ={true}
events ={modifiedEvents}
drop ={this.drop }
eventDrop ={this.eventDrop}
eventClick ={this.eventClick}
eventResize ={this.eventResize}
datesRender ={this.datesRender}
eventReceive ={this.eventReceive}
ref ={this.calendarComponentRef}
/>
<Module
modalData ={modalData}
isModalOpen ={isModalOpen}
deletingEventHandler ={this.deleterFlyer}
modalToggleHandler ={this.modalToggleHandler}
/>
<div className="calendarNotLoad">
<CalendarLoad />
</div>
<div className="mt-5">
<h4 className="mt-4 mb-4"><Trans>Is this Flyer ID exist in this view</Trans></h4>
<DebounceInput
className={`calendar-flyer-search ${isEventinTheView ? 'green' : 'red'}`}
type="text"
placeholder="Search for a Flyer in this month"
minLength={1}
debounceTimeout={700}
value={ isEventinTheViewValue }
onChange={ e => this.isEventinTheView(e.target.value) }
onBlur={ () => this.setState({ isEventinTheViewValue : '', isEventinTheView : null })}
/>
</div>
<div className="text-right">
<button className="btn mt-5 fc-prev-button fc-button fc-button-primary" onClick={ this.testingAndDebugging }>calendar.getEventSources()</button>
</div>
</div>
);
}
}
export default withAlert(CalendarDemo)
I add a button with a test function to start a playing around with the calendar functionality, and end up with two groups WORKING & NOT WORKING functions, like.
testingAndDebugging = () => {
const calendar = this.calendarComponentRef.current.getApi();
// WORKING EVENTS
//===============
calendar.render();
calendar.refetchEvents();
calendar.addEventSource(modifiedEvents);
calendar.batchRendering(function() {
calendar.changeView('timeGridWeek');
})
calendar.getEventSources();
calendar.on('dateClick', function(info) {
console.log('clicked view ' + info.view );
});
// NOT WORKING
//===============
calendar.removeEventSource();
calendar.removeEvents();
calendar.removeEvents(10157);
calendar.removeEvent();
calendar.removeEvent(10157);
}

A challenge with the use of filter

I am working on a react app which is a contact manager following teaching in a course by brad traversy, and while trying to use the filter array method to delete a profile , but its not working at all, telling me cannot read the property of filter.
import React, { Component } from 'react'
import Contact from './Contact'
class Contacts extends Component {
state ={
Contacts: [
{
id: 1,
name:"John Doe" ,
email:"olaidemmanuel0#gmail.com" ,
number:"08064965574"
},
{
id: 2,
name:"John Doe" ,
email:"olaidemmanuel1#gmail.com" ,
number:"08064965574"
},
{
id: 3,
name:"John Doe" ,
email:"olaidemmanuel#gmail.com" ,
number:"08064965574"
}
]
};
deleteContact = id => {
const { contacts } = this.state;
//this is where i had to use the arr.filter syntax
const newContacts = contacts.filter(contact => contact.id !== id);
this.setState({
contacts: newContacts
});
};
render() {
const { Contacts } = this.state;
return (
// <div>
<React.Fragment>
{Contacts.map(contact => (
<Contact
key={contact.id}
contact={contact}
deleteClickHandler=
{this.deleteContact.bind(this, contact.id)}
/> ))}
</React.Fragment>
/* </div > */
);
}
}
export default Contacts;
TypeError: Cannot read property 'filter' of undefined
Contacts is in uppercase in your state, try to rename it to contacts, otherwise it is undefined, check the case
You have a typo here for your state variable, you have state Contacts and trying to access contacts in function deleteContact, you need to do this,
deleteContact = id => {
const { Contacts } = this.state;
//this is where i had to use the arr.filter syntax
const newContacts = Contacts.filter(contact => contact.id !== id);
this.setState({
Contacts: newContacts
});
};
Demo

Deleting an item from my redux state gives multiple errors

I am having 2 issues:
Initially I can add clients to the empty array through action creators and my reducer. However, whenever I delete the items from the list and try to add new clients to it, it gives me an error: TypeError: Invalid attempt to spread non-iterable instance.
When I said I am deleting the items, what really happens is I create the clients, and then when I click on the delete button next to one of them, all of the clients delete. There is not error in the console, but I just want to delete the specific client with the corresponding id.
Here is my code!
Clients.js
import React, { Component } from 'react'
import AddClient from './AddClient'
import {connect} from 'react-redux'
import {deleteClient} from '../../store/actions/clientActions'
class Clients extends Component {
handleClick = (id) => {
console.log(id)
this.props.deleteClient(id)
}
render() {
const {clientList} = this.props
return (
<div className="container mt-5">
<h2>Here Are Your List of Clients...</h2>
{clientList && clientList.map(client => {
return(
<div key={client.id}>
<div>
Client Name: {client.name} | Client Price: {client.price}
<button onClick={() => {this.handleClick(client.id)}}>Delete</button>
</div>
</div>
)
})}
<AddClient/>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
clientList : state.clients.clientList,
}
}
const mapDispatchToProps = (dispatch) => {
return{
deleteClient : (id) => dispatch(deleteClient(id))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Clients)
Actions:
export const addClient = (client) => {
return(dispatch, getState) => {
dispatch({type: 'ADD CLIENT', client})
}
}
export const deleteClient = (id) => {
return(dispatch, getState) => {
dispatch({type: 'DELETE CLIENT', id})
}
}
Reducer:
const initState = {
clientList: []
}
const clientReducer = (state = initState, action) => {
switch (action.type) {
case 'ADD CLIENT' :
action.client.id = Math.random();
let clientList = [...state.clientList, action.client];
clientList.sort((a, b) => a.name.localeCompare(b.name));
return {
clientList
};
case 'DELETE CLIENT' :
const id = action.id;
clientList = state.clientList.filter(client =>
{return client.id !== id});
return clientList;
default : return state;
}
}
export default clientReducer
Lastly, this is AddClient.js
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {addClient} from '../../store/actions/clientActions'
class AddClient extends Component {
state = {
id: null,
name: null,
price: null,
}
handleChange = (e) => {
this.setState({
[e.target.id] : e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
this.props.addClient(this.state);
e.target.reset();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit} className="mt-5">
<h3>Add a new client:</h3>
<label htmlFor="name">Client Name: </label>
<input type="text" id="name" onChange={this.handleChange}/><br/>
<label htmlFor="price">Client Price: </label>
<input type="text" id="price" onChange={this.handleChange}/> <br/>
<button className="btn btn-primary">Add Client</button>
</form>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
addClient: (client) => dispatch(addClient(client))
}
}
export default connect(null, mapDispatchToProps)(AddClient)
Thank you for all the help, I am fairly new to React and Redux. Let me know if there is any other code you would like to see.
Here's how you can accomplish the delete:
export const deleteClient = (id) => {
const index = find the index of the client you want to delete from the array
return(dispatch, getState) => {
dispatch({type: 'DELETE CLIENT', index})
}
}
case 'DELETE CLIENT' :
return {
...state,
clientList: [
...state.clientList.slice(0, action.index),
...state.clientList.slice(action.index + 1)
]
}
I figured it out, the problem is within my clientReducer.js
This needs to change:
case 'DELETE CLIENT' :
const id = action.id;
clientList = state.clientList.filter(client =>
{return client.id !== id});
return clientList;
to...
case 'DELETE CLIENT' :
const id = action.id;
let newClientList = state.clientList.filter(client => {
return id !== client.id;
})
return {clientList : newClientList};
case 'DELETE CLIENT' :
const id = action.id;
const clientList = state.clientList.filter(client =>
{return client.id !== id});
return {
...state,
clientList
}
You're currently returning just an array, instead of an object. Since that's probably the only thing you have in your redux store right now, it's not breaking (in the ADD action), but you probably want to apply the previous state first, then add your newly filtered clientlist to the state you're returning.

Redux action not firing on move with react-dnd

I'm pretty new to React and Redux and very new to react-dnd, and I think I'm doing something wildly incorrect here. Although there are other similar posts out there I can't quite find a solution in them.
I'm working on a Kanban board app that is somewhat based on the one found at https://survivejs.com/react/implementing-kanban/drag-and-drop/ though that version uses Alt.js and I'm using Redux.
The problem: when dragging a component, the action function is called but the case in the reducer (MOVE_TICKET) is not. This seems to be the case regardless of the content of the action function.
I linked the action to a click event and in this instance the action and reducer worked as expected. This leads me to think that it must be a problem with the way I've set up the Ticket component with the dnd functions.
Ticket.js:
import React from "react"
import {compose} from 'redux';
import { DragSource, DropTarget } from 'react-dnd';
import ItemTypes from '../constants/ItemTypes';
import { moveTicket } from "../actions/ticketsActions"
const Ticket = ({
connectDragSource, connectDropTarget, isDragging, isOver, onMove, id, children, ...props
}) => {
return compose (connectDragSource, connectDropTarget)(
<div style={{
opacity: isDragging || isOver ? 0 : 1
}} { ...props } className = 'ticket'>
<h3 className = 'summary'> { props.summary } </h3>
<span className = 'projectName'> { props.projectName }</span>
<span className = 'assignee'> { props.assignee } </span>
<span className = 'priority'> { props.priority } </span>
</div>
);
};
const ticketSource = {
beginDrag(props) {
return {
id: props.id,
status: props.status
};
}
};
const ticketTarget = {
hover(targetProps, monitor) {
const targetId = targetProps.id;
const sourceProps = monitor.getItem();
const sourceId = sourceProps.id;
const sourceCol = sourceProps.status;
const targetCol = targetProps.status;
if(sourceId !== targetId) {
targetProps.onMove({sourceId, targetId, sourceCol, targetCol});
}
}
};
export default compose(
DragSource(ItemTypes.TICKET, ticketSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
})),
DropTarget(ItemTypes.TICKET, ticketTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver()
}))
)(Ticket)
ticketsReducer.js:
export default function reducer(state={
tickets: [],
fetching: false,
fetched: false,
error: null,
}, action) {
switch (action.type) {
case "MOVE_TICKET": {
return [{...state, tickets: action.payload}]
}
}
return state
}
ticketsActions.js
import store from '../store';
export function moveTicket({sourceId, targetId, sourceCol, targetCol}) {
const columns = Object.assign({}, store.getState().tickets.tickets)
const sourceList = columns[sourceCol];
const targetList = columns[targetCol];
const sourceTicketIndex = sourceList.findIndex(ticket => ticket.id == sourceId);
const targetTicketIndex = targetList.findIndex(ticket => ticket.id == targetId);
if(sourceCol === targetCol){
var arrayClone = sourceList.slice();
arrayClone.splice(sourceTicketIndex, 1);
arrayClone.splice(targetTicketIndex, 0, sourceList[sourceTicketIndex]);
columns[sourceCol] = arrayClone;
}
return function(dispatch){
dispatch({type: "MOVE_TICKET", payload: columns});
}
}
Column.js (where each Ticket component is rendered)
import React from "react"
import uuid from "uuid"
import { connect } from "react-redux"
import ColumnsContainer from "./ColumnsContainer"
import Ticket from "./ticket"
import { moveTicket } from "../actions/ticketsActions"
#connect((store) => {
return {
columns: store.columns.columns
};
})
export default class Column extends React.Component {
console(){
console.log(this)
}
render(){
const tickets = this.props.tickets.map((ticket, id) =>
<Ticket
key = {uuid.v4()}
id={ticket.id}
summary = { ticket.summary }
assignee = { ticket.assignee }
priority = { ticket.priority }
projectName = { ticket.displayName }
onMove={ moveTicket }
status= { ticket.status }
/>
)
return(
<div key = {uuid.v4()} className = { this.props.className }>
<h2 key = {uuid.v4()}>{ this.props.title }</h2>
<ul key = {uuid.v4()}>{ tickets }</ul>
</div>
)
}
}
If anyone can see where I'm going wrong I could really use some assistance.
You are not connecting the moveTicket action to redux's dispatcher.
You'll have to do something like:
#connect((store) => {
return {
columns: store.columns.columns
};
}, {moveTicket})
export default class Column extends React.Component {
// ...
// use this.props.moveTicket instead of moveTicket
The second parameter to connect is called mapDispatchToProps, which will do the dispatch(actionFn) for you.
You might want to name the bound action differently, e.g.
#connect((store) => {
return {
columns: store.columns.columns
};
}, {connectedMoveTicket: moveTicket})
// then use this.props.connectedMoveTicket

Resources