React Redux - Table of select dropdowns with mutually exclusive options












1














I'm scratching my head on this one. And i find it's also not easy to explain. I'll do my best:



I have an html table, each row has an image and, amongst other elements, also a select dropdown with a top 10 list, to rank the image.
When a user selects a ranking, the database gets updated accordingly ->
The current image top 10 ranking is saved in the image entry, and the rank of the former image to inherit the position gets updated to 'null'. (this is already working -> so if I reload the page, everything turns up fine).
What I'm unable to achieve, is for the updated images array that I receive back from the db to update the state (or the props) and therefor the selected option value of the image that formerly inherited the rank.
Here's my ImageList Component (the important parts):



class ImageList extends Component {


constructor(props) {
super(props)
this.state = {
project: ,
description: '',
name: '',
values: ,
value: '',
positions: props.positions
}
}

updatePosition = (projectId, projectName, imageId, imgName, i, e) => {

this.props.setGridPosition(
projectId,
projectName,
imageId,
imgName,
e.target.value
)
}

getAllImages() {
let imageList =
if (this.props.project.project) {
const { project, waiting } = this.props.project

for (let [i, img] of project.images.entries()) {
if (!img.isDeleted) {
let options = ['-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
this.props.positions[i] = img.gridPosition

let imgSrc = `/public/${project._id}/${img.originalName}`
imageList.push(
<tr
key={img._id}
style={waiting ? { opacity: '.5' } : { opacity: '1' }}
>
<td>
<img src={imgSrc} alt="" style={{ width: '60px' }} />
</td>

<SelectFieldGroup
name={`placeInGrid_${i}`}
onChange={this.updatePosition.bind(
this,
project._id,
project.name,
img._id,
img.originalName,
i
)}
options={options}
value={this.props.positions[i]}
/>
</td>

</tr>
)
}
}
}
return imageList
}

render() {
return (
<div className={styles['image-list']}>
<table className={styles['image-table']}>
<tbody>{this.getAllImages()}</tbody>
</table>
</div>
)
}
}

const mapStateToProps = state => ({
auth: state.auth,
project: state.project
})

export default connect(
mapStateToProps,
{ deleteImage, setGridPosition }
)(ImageList)


I receive the props - the project and positions (as an empty array) - from the parent Component.



I hope the issue is somehow clear. I would really appreciate any help or pointers to where I went wrong.



Edit:
As requested, for clarification, here are some other parts of the code:



SelectFieldGroup.js:



import React from 'react'
import PropTypes from 'prop-types'

import cx from 'classnames'
import globalStyles from './Bootstrap.module.css'
import commonStyles from './Common.module.sass'

const SelectFieldGroup = ({ name, onChange, options, value, disabled }) => {
let optionArray =
for (let [index, option] of options.entries()) {
optionArray.push(<option key={index}>{option}</option>)
}

return (
<div className={globalStyles['form-group']}>
<select
value={value}
className={cx(
globalStyles['custom-select'],
commonStyles['custom-select'],
commonStyles['dark-input']
)}
name={name}
onChange={onChange}
disabled={disabled}
>
{optionArray}
</select>
</div>
)
}

SelectFieldGroup.propTypes = {
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.string
}

export default SelectFieldGroup


The relevant part of imageActions.:



export const setGridPosition = (
projectId,
projectName,
imageId,
imageName,
position
) => dispatch => {
dispatch(setWaiting())
const data = {
projectId: projectId,
projectName: projectName,
imageId: imageId,
imageName: imageName,
position: position
}
console.log(projectId)
axios
.post('/api/projects/set_grid_position', data)
.then(res => {
console.log(res.data)
dispatch({
type: SET_GRID_POSITION,
payload: res.data
})
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: {}
})
)
}


The node express api:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const errors = {}
Project.findById(req.body.projectId).then(currentProject => {
let updatedProject = currentProject
ProjectGridPosition.findOne({ position: req.body.position }).then(
gridPosition => {
if (req.body.position != '-') {
// Mark the previous position of the image as empty.
ProjectGridPosition.findOne({ imageId: req.body.imageId })
.then(oldPos => {
oldPos.isTaken = false
oldPos.save()
})
.catch(err => res.status(400).json(err))
// Set the gridPosition inside the image.
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
else {
updatedProject = currentProject
}
})

if (gridPosition) {
if (gridPosition.projectId) {
Project.findById(gridPosition.projectId)
.then(project => {
console.log(project.name)
project.images.forEach(img => {
if (img.gridPosition == req.body.position) {
console.log(img.originalName)
img.gridPosition = '-'
}
})
project.save(err => {
if (err) {
res.json(err)
} else {
if (project == currentProject) {
updatedProject = currentProject
}
}
})
})
.catch(err => res.json(err))
}
gridPosition.projectId = req.body.projectId
gridPosition.projectName = req.body.projectName
gridPosition.imageId = req.body.imageId
gridPosition.imageName = req.body.imageName
gridPosition.isTaken = true
gridPosition.save()

res.json(updatedProject)
} else {
const newPosFields = {
projectId: req.body.projectId,
projectName: req.body.projectName,
imageId: req.body.imageId,
imageName: req.body.imageName,
position: req.body.position,
isTaken: true
}
new ProjectGridPosition(newPosFields)
.save()
.then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})

.catch(err => res.json(err))
}
} else {
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
ProjectGridPosition.findOne({ imageId: req.body.imageId }).then(
newPos => {
newPos.isTaken = false
newPos.save().then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})
}
)
})
}
}
)
})
}
)


And finally, the relevant part of projectReducer.js:



import {
// ...
SET_GRID_POSITION
} from '../actions/types'

const initialState = {
project: null,
projects: null,
loading: false,
waiting: false
}

export default function(state = initialState, action) {
switch (action.type) {

// ....

case SET_GRID_POSITION:
return {
...state,
project: action.payload,
waiting: false
}
default:
return state
}
}









share|improve this question
























  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 '18 at 22:49










  • can you add your reducer code where you are saving your update images array and also the setGridPosition action code in order to have a better view at your state management.
    – Pranay Tripathi
    Nov 22 '18 at 0:41










  • @Tex Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • Thanks for sharing the additional code. The challenge now is that there's quite a lot of code to try to digest all at once. I think the source of your problem, though, is in getAllImages, specifically here: this.props.positions[i] = img.gridPosition. One should never mutate props directly. Rather, one should update the state (either component state via setState or Redux*) and let React (and potentially Redux) take care of updating the component when the positions change. * If I had to choose between component state and Redux, I'd definitely choose Redux here.
    – Tex
    Nov 23 '18 at 8:05


















1














I'm scratching my head on this one. And i find it's also not easy to explain. I'll do my best:



I have an html table, each row has an image and, amongst other elements, also a select dropdown with a top 10 list, to rank the image.
When a user selects a ranking, the database gets updated accordingly ->
The current image top 10 ranking is saved in the image entry, and the rank of the former image to inherit the position gets updated to 'null'. (this is already working -> so if I reload the page, everything turns up fine).
What I'm unable to achieve, is for the updated images array that I receive back from the db to update the state (or the props) and therefor the selected option value of the image that formerly inherited the rank.
Here's my ImageList Component (the important parts):



class ImageList extends Component {


constructor(props) {
super(props)
this.state = {
project: ,
description: '',
name: '',
values: ,
value: '',
positions: props.positions
}
}

updatePosition = (projectId, projectName, imageId, imgName, i, e) => {

this.props.setGridPosition(
projectId,
projectName,
imageId,
imgName,
e.target.value
)
}

getAllImages() {
let imageList =
if (this.props.project.project) {
const { project, waiting } = this.props.project

for (let [i, img] of project.images.entries()) {
if (!img.isDeleted) {
let options = ['-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
this.props.positions[i] = img.gridPosition

let imgSrc = `/public/${project._id}/${img.originalName}`
imageList.push(
<tr
key={img._id}
style={waiting ? { opacity: '.5' } : { opacity: '1' }}
>
<td>
<img src={imgSrc} alt="" style={{ width: '60px' }} />
</td>

<SelectFieldGroup
name={`placeInGrid_${i}`}
onChange={this.updatePosition.bind(
this,
project._id,
project.name,
img._id,
img.originalName,
i
)}
options={options}
value={this.props.positions[i]}
/>
</td>

</tr>
)
}
}
}
return imageList
}

render() {
return (
<div className={styles['image-list']}>
<table className={styles['image-table']}>
<tbody>{this.getAllImages()}</tbody>
</table>
</div>
)
}
}

const mapStateToProps = state => ({
auth: state.auth,
project: state.project
})

export default connect(
mapStateToProps,
{ deleteImage, setGridPosition }
)(ImageList)


I receive the props - the project and positions (as an empty array) - from the parent Component.



I hope the issue is somehow clear. I would really appreciate any help or pointers to where I went wrong.



Edit:
As requested, for clarification, here are some other parts of the code:



SelectFieldGroup.js:



import React from 'react'
import PropTypes from 'prop-types'

import cx from 'classnames'
import globalStyles from './Bootstrap.module.css'
import commonStyles from './Common.module.sass'

const SelectFieldGroup = ({ name, onChange, options, value, disabled }) => {
let optionArray =
for (let [index, option] of options.entries()) {
optionArray.push(<option key={index}>{option}</option>)
}

return (
<div className={globalStyles['form-group']}>
<select
value={value}
className={cx(
globalStyles['custom-select'],
commonStyles['custom-select'],
commonStyles['dark-input']
)}
name={name}
onChange={onChange}
disabled={disabled}
>
{optionArray}
</select>
</div>
)
}

SelectFieldGroup.propTypes = {
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.string
}

export default SelectFieldGroup


The relevant part of imageActions.:



export const setGridPosition = (
projectId,
projectName,
imageId,
imageName,
position
) => dispatch => {
dispatch(setWaiting())
const data = {
projectId: projectId,
projectName: projectName,
imageId: imageId,
imageName: imageName,
position: position
}
console.log(projectId)
axios
.post('/api/projects/set_grid_position', data)
.then(res => {
console.log(res.data)
dispatch({
type: SET_GRID_POSITION,
payload: res.data
})
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: {}
})
)
}


The node express api:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const errors = {}
Project.findById(req.body.projectId).then(currentProject => {
let updatedProject = currentProject
ProjectGridPosition.findOne({ position: req.body.position }).then(
gridPosition => {
if (req.body.position != '-') {
// Mark the previous position of the image as empty.
ProjectGridPosition.findOne({ imageId: req.body.imageId })
.then(oldPos => {
oldPos.isTaken = false
oldPos.save()
})
.catch(err => res.status(400).json(err))
// Set the gridPosition inside the image.
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
else {
updatedProject = currentProject
}
})

if (gridPosition) {
if (gridPosition.projectId) {
Project.findById(gridPosition.projectId)
.then(project => {
console.log(project.name)
project.images.forEach(img => {
if (img.gridPosition == req.body.position) {
console.log(img.originalName)
img.gridPosition = '-'
}
})
project.save(err => {
if (err) {
res.json(err)
} else {
if (project == currentProject) {
updatedProject = currentProject
}
}
})
})
.catch(err => res.json(err))
}
gridPosition.projectId = req.body.projectId
gridPosition.projectName = req.body.projectName
gridPosition.imageId = req.body.imageId
gridPosition.imageName = req.body.imageName
gridPosition.isTaken = true
gridPosition.save()

res.json(updatedProject)
} else {
const newPosFields = {
projectId: req.body.projectId,
projectName: req.body.projectName,
imageId: req.body.imageId,
imageName: req.body.imageName,
position: req.body.position,
isTaken: true
}
new ProjectGridPosition(newPosFields)
.save()
.then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})

.catch(err => res.json(err))
}
} else {
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
ProjectGridPosition.findOne({ imageId: req.body.imageId }).then(
newPos => {
newPos.isTaken = false
newPos.save().then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})
}
)
})
}
}
)
})
}
)


And finally, the relevant part of projectReducer.js:



import {
// ...
SET_GRID_POSITION
} from '../actions/types'

const initialState = {
project: null,
projects: null,
loading: false,
waiting: false
}

export default function(state = initialState, action) {
switch (action.type) {

// ....

case SET_GRID_POSITION:
return {
...state,
project: action.payload,
waiting: false
}
default:
return state
}
}









share|improve this question
























  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 '18 at 22:49










  • can you add your reducer code where you are saving your update images array and also the setGridPosition action code in order to have a better view at your state management.
    – Pranay Tripathi
    Nov 22 '18 at 0:41










  • @Tex Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • Thanks for sharing the additional code. The challenge now is that there's quite a lot of code to try to digest all at once. I think the source of your problem, though, is in getAllImages, specifically here: this.props.positions[i] = img.gridPosition. One should never mutate props directly. Rather, one should update the state (either component state via setState or Redux*) and let React (and potentially Redux) take care of updating the component when the positions change. * If I had to choose between component state and Redux, I'd definitely choose Redux here.
    – Tex
    Nov 23 '18 at 8:05
















1












1








1







I'm scratching my head on this one. And i find it's also not easy to explain. I'll do my best:



I have an html table, each row has an image and, amongst other elements, also a select dropdown with a top 10 list, to rank the image.
When a user selects a ranking, the database gets updated accordingly ->
The current image top 10 ranking is saved in the image entry, and the rank of the former image to inherit the position gets updated to 'null'. (this is already working -> so if I reload the page, everything turns up fine).
What I'm unable to achieve, is for the updated images array that I receive back from the db to update the state (or the props) and therefor the selected option value of the image that formerly inherited the rank.
Here's my ImageList Component (the important parts):



class ImageList extends Component {


constructor(props) {
super(props)
this.state = {
project: ,
description: '',
name: '',
values: ,
value: '',
positions: props.positions
}
}

updatePosition = (projectId, projectName, imageId, imgName, i, e) => {

this.props.setGridPosition(
projectId,
projectName,
imageId,
imgName,
e.target.value
)
}

getAllImages() {
let imageList =
if (this.props.project.project) {
const { project, waiting } = this.props.project

for (let [i, img] of project.images.entries()) {
if (!img.isDeleted) {
let options = ['-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
this.props.positions[i] = img.gridPosition

let imgSrc = `/public/${project._id}/${img.originalName}`
imageList.push(
<tr
key={img._id}
style={waiting ? { opacity: '.5' } : { opacity: '1' }}
>
<td>
<img src={imgSrc} alt="" style={{ width: '60px' }} />
</td>

<SelectFieldGroup
name={`placeInGrid_${i}`}
onChange={this.updatePosition.bind(
this,
project._id,
project.name,
img._id,
img.originalName,
i
)}
options={options}
value={this.props.positions[i]}
/>
</td>

</tr>
)
}
}
}
return imageList
}

render() {
return (
<div className={styles['image-list']}>
<table className={styles['image-table']}>
<tbody>{this.getAllImages()}</tbody>
</table>
</div>
)
}
}

const mapStateToProps = state => ({
auth: state.auth,
project: state.project
})

export default connect(
mapStateToProps,
{ deleteImage, setGridPosition }
)(ImageList)


I receive the props - the project and positions (as an empty array) - from the parent Component.



I hope the issue is somehow clear. I would really appreciate any help or pointers to where I went wrong.



Edit:
As requested, for clarification, here are some other parts of the code:



SelectFieldGroup.js:



import React from 'react'
import PropTypes from 'prop-types'

import cx from 'classnames'
import globalStyles from './Bootstrap.module.css'
import commonStyles from './Common.module.sass'

const SelectFieldGroup = ({ name, onChange, options, value, disabled }) => {
let optionArray =
for (let [index, option] of options.entries()) {
optionArray.push(<option key={index}>{option}</option>)
}

return (
<div className={globalStyles['form-group']}>
<select
value={value}
className={cx(
globalStyles['custom-select'],
commonStyles['custom-select'],
commonStyles['dark-input']
)}
name={name}
onChange={onChange}
disabled={disabled}
>
{optionArray}
</select>
</div>
)
}

SelectFieldGroup.propTypes = {
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.string
}

export default SelectFieldGroup


The relevant part of imageActions.:



export const setGridPosition = (
projectId,
projectName,
imageId,
imageName,
position
) => dispatch => {
dispatch(setWaiting())
const data = {
projectId: projectId,
projectName: projectName,
imageId: imageId,
imageName: imageName,
position: position
}
console.log(projectId)
axios
.post('/api/projects/set_grid_position', data)
.then(res => {
console.log(res.data)
dispatch({
type: SET_GRID_POSITION,
payload: res.data
})
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: {}
})
)
}


The node express api:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const errors = {}
Project.findById(req.body.projectId).then(currentProject => {
let updatedProject = currentProject
ProjectGridPosition.findOne({ position: req.body.position }).then(
gridPosition => {
if (req.body.position != '-') {
// Mark the previous position of the image as empty.
ProjectGridPosition.findOne({ imageId: req.body.imageId })
.then(oldPos => {
oldPos.isTaken = false
oldPos.save()
})
.catch(err => res.status(400).json(err))
// Set the gridPosition inside the image.
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
else {
updatedProject = currentProject
}
})

if (gridPosition) {
if (gridPosition.projectId) {
Project.findById(gridPosition.projectId)
.then(project => {
console.log(project.name)
project.images.forEach(img => {
if (img.gridPosition == req.body.position) {
console.log(img.originalName)
img.gridPosition = '-'
}
})
project.save(err => {
if (err) {
res.json(err)
} else {
if (project == currentProject) {
updatedProject = currentProject
}
}
})
})
.catch(err => res.json(err))
}
gridPosition.projectId = req.body.projectId
gridPosition.projectName = req.body.projectName
gridPosition.imageId = req.body.imageId
gridPosition.imageName = req.body.imageName
gridPosition.isTaken = true
gridPosition.save()

res.json(updatedProject)
} else {
const newPosFields = {
projectId: req.body.projectId,
projectName: req.body.projectName,
imageId: req.body.imageId,
imageName: req.body.imageName,
position: req.body.position,
isTaken: true
}
new ProjectGridPosition(newPosFields)
.save()
.then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})

.catch(err => res.json(err))
}
} else {
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
ProjectGridPosition.findOne({ imageId: req.body.imageId }).then(
newPos => {
newPos.isTaken = false
newPos.save().then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})
}
)
})
}
}
)
})
}
)


And finally, the relevant part of projectReducer.js:



import {
// ...
SET_GRID_POSITION
} from '../actions/types'

const initialState = {
project: null,
projects: null,
loading: false,
waiting: false
}

export default function(state = initialState, action) {
switch (action.type) {

// ....

case SET_GRID_POSITION:
return {
...state,
project: action.payload,
waiting: false
}
default:
return state
}
}









share|improve this question















I'm scratching my head on this one. And i find it's also not easy to explain. I'll do my best:



I have an html table, each row has an image and, amongst other elements, also a select dropdown with a top 10 list, to rank the image.
When a user selects a ranking, the database gets updated accordingly ->
The current image top 10 ranking is saved in the image entry, and the rank of the former image to inherit the position gets updated to 'null'. (this is already working -> so if I reload the page, everything turns up fine).
What I'm unable to achieve, is for the updated images array that I receive back from the db to update the state (or the props) and therefor the selected option value of the image that formerly inherited the rank.
Here's my ImageList Component (the important parts):



class ImageList extends Component {


constructor(props) {
super(props)
this.state = {
project: ,
description: '',
name: '',
values: ,
value: '',
positions: props.positions
}
}

updatePosition = (projectId, projectName, imageId, imgName, i, e) => {

this.props.setGridPosition(
projectId,
projectName,
imageId,
imgName,
e.target.value
)
}

getAllImages() {
let imageList =
if (this.props.project.project) {
const { project, waiting } = this.props.project

for (let [i, img] of project.images.entries()) {
if (!img.isDeleted) {
let options = ['-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
this.props.positions[i] = img.gridPosition

let imgSrc = `/public/${project._id}/${img.originalName}`
imageList.push(
<tr
key={img._id}
style={waiting ? { opacity: '.5' } : { opacity: '1' }}
>
<td>
<img src={imgSrc} alt="" style={{ width: '60px' }} />
</td>

<SelectFieldGroup
name={`placeInGrid_${i}`}
onChange={this.updatePosition.bind(
this,
project._id,
project.name,
img._id,
img.originalName,
i
)}
options={options}
value={this.props.positions[i]}
/>
</td>

</tr>
)
}
}
}
return imageList
}

render() {
return (
<div className={styles['image-list']}>
<table className={styles['image-table']}>
<tbody>{this.getAllImages()}</tbody>
</table>
</div>
)
}
}

const mapStateToProps = state => ({
auth: state.auth,
project: state.project
})

export default connect(
mapStateToProps,
{ deleteImage, setGridPosition }
)(ImageList)


I receive the props - the project and positions (as an empty array) - from the parent Component.



I hope the issue is somehow clear. I would really appreciate any help or pointers to where I went wrong.



Edit:
As requested, for clarification, here are some other parts of the code:



SelectFieldGroup.js:



import React from 'react'
import PropTypes from 'prop-types'

import cx from 'classnames'
import globalStyles from './Bootstrap.module.css'
import commonStyles from './Common.module.sass'

const SelectFieldGroup = ({ name, onChange, options, value, disabled }) => {
let optionArray =
for (let [index, option] of options.entries()) {
optionArray.push(<option key={index}>{option}</option>)
}

return (
<div className={globalStyles['form-group']}>
<select
value={value}
className={cx(
globalStyles['custom-select'],
commonStyles['custom-select'],
commonStyles['dark-input']
)}
name={name}
onChange={onChange}
disabled={disabled}
>
{optionArray}
</select>
</div>
)
}

SelectFieldGroup.propTypes = {
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.string
}

export default SelectFieldGroup


The relevant part of imageActions.:



export const setGridPosition = (
projectId,
projectName,
imageId,
imageName,
position
) => dispatch => {
dispatch(setWaiting())
const data = {
projectId: projectId,
projectName: projectName,
imageId: imageId,
imageName: imageName,
position: position
}
console.log(projectId)
axios
.post('/api/projects/set_grid_position', data)
.then(res => {
console.log(res.data)
dispatch({
type: SET_GRID_POSITION,
payload: res.data
})
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: {}
})
)
}


The node express api:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const errors = {}
Project.findById(req.body.projectId).then(currentProject => {
let updatedProject = currentProject
ProjectGridPosition.findOne({ position: req.body.position }).then(
gridPosition => {
if (req.body.position != '-') {
// Mark the previous position of the image as empty.
ProjectGridPosition.findOne({ imageId: req.body.imageId })
.then(oldPos => {
oldPos.isTaken = false
oldPos.save()
})
.catch(err => res.status(400).json(err))
// Set the gridPosition inside the image.
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
else {
updatedProject = currentProject
}
})

if (gridPosition) {
if (gridPosition.projectId) {
Project.findById(gridPosition.projectId)
.then(project => {
console.log(project.name)
project.images.forEach(img => {
if (img.gridPosition == req.body.position) {
console.log(img.originalName)
img.gridPosition = '-'
}
})
project.save(err => {
if (err) {
res.json(err)
} else {
if (project == currentProject) {
updatedProject = currentProject
}
}
})
})
.catch(err => res.json(err))
}
gridPosition.projectId = req.body.projectId
gridPosition.projectName = req.body.projectName
gridPosition.imageId = req.body.imageId
gridPosition.imageName = req.body.imageName
gridPosition.isTaken = true
gridPosition.save()

res.json(updatedProject)
} else {
const newPosFields = {
projectId: req.body.projectId,
projectName: req.body.projectName,
imageId: req.body.imageId,
imageName: req.body.imageName,
position: req.body.position,
isTaken: true
}
new ProjectGridPosition(newPosFields)
.save()
.then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})

.catch(err => res.json(err))
}
} else {
currentProject.images.forEach(img => {
if (img._id == req.body.imageId) {
img.gridPosition = req.body.position
}
})
currentProject.save(err => {
if (err) res.json(err)
ProjectGridPosition.findOne({ imageId: req.body.imageId }).then(
newPos => {
newPos.isTaken = false
newPos.save().then(() => {
currentProject.save().then(() => {
res.json(currentProject)
})
})
}
)
})
}
}
)
})
}
)


And finally, the relevant part of projectReducer.js:



import {
// ...
SET_GRID_POSITION
} from '../actions/types'

const initialState = {
project: null,
projects: null,
loading: false,
waiting: false
}

export default function(state = initialState, action) {
switch (action.type) {

// ....

case SET_GRID_POSITION:
return {
...state,
project: action.payload,
waiting: false
}
default:
return state
}
}






reactjs react-redux






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 22 '18 at 10:11







mctoothpick

















asked Nov 21 '18 at 22:28









mctoothpickmctoothpick

62




62












  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 '18 at 22:49










  • can you add your reducer code where you are saving your update images array and also the setGridPosition action code in order to have a better view at your state management.
    – Pranay Tripathi
    Nov 22 '18 at 0:41










  • @Tex Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • Thanks for sharing the additional code. The challenge now is that there's quite a lot of code to try to digest all at once. I think the source of your problem, though, is in getAllImages, specifically here: this.props.positions[i] = img.gridPosition. One should never mutate props directly. Rather, one should update the state (either component state via setState or Redux*) and let React (and potentially Redux) take care of updating the component when the positions change. * If I had to choose between component state and Redux, I'd definitely choose Redux here.
    – Tex
    Nov 23 '18 at 8:05




















  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 '18 at 22:49










  • can you add your reducer code where you are saving your update images array and also the setGridPosition action code in order to have a better view at your state management.
    – Pranay Tripathi
    Nov 22 '18 at 0:41










  • @Tex Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 '18 at 8:54










  • Thanks for sharing the additional code. The challenge now is that there's quite a lot of code to try to digest all at once. I think the source of your problem, though, is in getAllImages, specifically here: this.props.positions[i] = img.gridPosition. One should never mutate props directly. Rather, one should update the state (either component state via setState or Redux*) and let React (and potentially Redux) take care of updating the component when the positions change. * If I had to choose between component state and Redux, I'd definitely choose Redux here.
    – Tex
    Nov 23 '18 at 8:05


















It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
– Tex
Nov 21 '18 at 22:49




It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
– Tex
Nov 21 '18 at 22:49












can you add your reducer code where you are saving your update images array and also the setGridPosition action code in order to have a better view at your state management.
– Pranay Tripathi
Nov 22 '18 at 0:41




can you add your reducer code where you are saving your update images array and also the setGridPosition action code in order to have a better view at your state management.
– Pranay Tripathi
Nov 22 '18 at 0:41












@Tex Thanks, I edited the question!
– mctoothpick
Nov 22 '18 at 8:54




@Tex Thanks, I edited the question!
– mctoothpick
Nov 22 '18 at 8:54












@PranayTripathi Thanks, I edited the question!
– mctoothpick
Nov 22 '18 at 8:54




@PranayTripathi Thanks, I edited the question!
– mctoothpick
Nov 22 '18 at 8:54












Thanks for sharing the additional code. The challenge now is that there's quite a lot of code to try to digest all at once. I think the source of your problem, though, is in getAllImages, specifically here: this.props.positions[i] = img.gridPosition. One should never mutate props directly. Rather, one should update the state (either component state via setState or Redux*) and let React (and potentially Redux) take care of updating the component when the positions change. * If I had to choose between component state and Redux, I'd definitely choose Redux here.
– Tex
Nov 23 '18 at 8:05






Thanks for sharing the additional code. The challenge now is that there's quite a lot of code to try to digest all at once. I think the source of your problem, though, is in getAllImages, specifically here: this.props.positions[i] = img.gridPosition. One should never mutate props directly. Rather, one should update the state (either component state via setState or Redux*) and let React (and potentially Redux) take care of updating the component when the positions change. * If I had to choose between component state and Redux, I'd definitely choose Redux here.
– Tex
Nov 23 '18 at 8:05














1 Answer
1






active

oldest

votes


















0














So I managed to make it work by restructuring and getting rid of the ProjectGridPosition model completely. Doing so makes the whole process a lot simpler. I then completely rewrote the route:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
async (req, res) => {
let project = await getProjectById(req.body.projectId)
const query = {
'images.gridPosition': req.body.position
}
let formerRankProject = await getProjectByQuery(query)

project = await updateRank(project, req.body.imageId, req.body.position)

if (formerRankProject !== null) {
formerRankProject = await UpdateIfDifferentProject(
formerRankProject,
project._id,
req.body
)
formerRankProject.save()
}

project
.save()
.then(project => res.json(project))
.catch(err => res.json(err))
}
)


Now it's working. I don't exactly know what the problem was, but as @Tex pointed out in the comments, I had a LOT of levels of nesting - so something probably was bound to go wrong.



I will mark this as the correct answer - even though it's more of a work around - so people know, I'm not still looking for help.






share|improve this answer





















  • Now that looks much better - good work!
    – Tex
    Nov 23 '18 at 18:20











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421328%2freact-redux-table-of-select-dropdowns-with-mutually-exclusive-options%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














So I managed to make it work by restructuring and getting rid of the ProjectGridPosition model completely. Doing so makes the whole process a lot simpler. I then completely rewrote the route:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
async (req, res) => {
let project = await getProjectById(req.body.projectId)
const query = {
'images.gridPosition': req.body.position
}
let formerRankProject = await getProjectByQuery(query)

project = await updateRank(project, req.body.imageId, req.body.position)

if (formerRankProject !== null) {
formerRankProject = await UpdateIfDifferentProject(
formerRankProject,
project._id,
req.body
)
formerRankProject.save()
}

project
.save()
.then(project => res.json(project))
.catch(err => res.json(err))
}
)


Now it's working. I don't exactly know what the problem was, but as @Tex pointed out in the comments, I had a LOT of levels of nesting - so something probably was bound to go wrong.



I will mark this as the correct answer - even though it's more of a work around - so people know, I'm not still looking for help.






share|improve this answer





















  • Now that looks much better - good work!
    – Tex
    Nov 23 '18 at 18:20
















0














So I managed to make it work by restructuring and getting rid of the ProjectGridPosition model completely. Doing so makes the whole process a lot simpler. I then completely rewrote the route:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
async (req, res) => {
let project = await getProjectById(req.body.projectId)
const query = {
'images.gridPosition': req.body.position
}
let formerRankProject = await getProjectByQuery(query)

project = await updateRank(project, req.body.imageId, req.body.position)

if (formerRankProject !== null) {
formerRankProject = await UpdateIfDifferentProject(
formerRankProject,
project._id,
req.body
)
formerRankProject.save()
}

project
.save()
.then(project => res.json(project))
.catch(err => res.json(err))
}
)


Now it's working. I don't exactly know what the problem was, but as @Tex pointed out in the comments, I had a LOT of levels of nesting - so something probably was bound to go wrong.



I will mark this as the correct answer - even though it's more of a work around - so people know, I'm not still looking for help.






share|improve this answer





















  • Now that looks much better - good work!
    – Tex
    Nov 23 '18 at 18:20














0












0








0






So I managed to make it work by restructuring and getting rid of the ProjectGridPosition model completely. Doing so makes the whole process a lot simpler. I then completely rewrote the route:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
async (req, res) => {
let project = await getProjectById(req.body.projectId)
const query = {
'images.gridPosition': req.body.position
}
let formerRankProject = await getProjectByQuery(query)

project = await updateRank(project, req.body.imageId, req.body.position)

if (formerRankProject !== null) {
formerRankProject = await UpdateIfDifferentProject(
formerRankProject,
project._id,
req.body
)
formerRankProject.save()
}

project
.save()
.then(project => res.json(project))
.catch(err => res.json(err))
}
)


Now it's working. I don't exactly know what the problem was, but as @Tex pointed out in the comments, I had a LOT of levels of nesting - so something probably was bound to go wrong.



I will mark this as the correct answer - even though it's more of a work around - so people know, I'm not still looking for help.






share|improve this answer












So I managed to make it work by restructuring and getting rid of the ProjectGridPosition model completely. Doing so makes the whole process a lot simpler. I then completely rewrote the route:



router.post(
'/set_grid_position',
passport.authenticate('jwt', { session: false }),
async (req, res) => {
let project = await getProjectById(req.body.projectId)
const query = {
'images.gridPosition': req.body.position
}
let formerRankProject = await getProjectByQuery(query)

project = await updateRank(project, req.body.imageId, req.body.position)

if (formerRankProject !== null) {
formerRankProject = await UpdateIfDifferentProject(
formerRankProject,
project._id,
req.body
)
formerRankProject.save()
}

project
.save()
.then(project => res.json(project))
.catch(err => res.json(err))
}
)


Now it's working. I don't exactly know what the problem was, but as @Tex pointed out in the comments, I had a LOT of levels of nesting - so something probably was bound to go wrong.



I will mark this as the correct answer - even though it's more of a work around - so people know, I'm not still looking for help.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 23 '18 at 17:55









mctoothpickmctoothpick

62




62












  • Now that looks much better - good work!
    – Tex
    Nov 23 '18 at 18:20


















  • Now that looks much better - good work!
    – Tex
    Nov 23 '18 at 18:20
















Now that looks much better - good work!
– Tex
Nov 23 '18 at 18:20




Now that looks much better - good work!
– Tex
Nov 23 '18 at 18:20


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421328%2freact-redux-table-of-select-dropdowns-with-mutually-exclusive-options%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'