React Redux - Table of select dropdowns with mutually exclusive options











up vote
1
down vote

favorite












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









New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 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 at 0:41










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










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 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 at 8:05

















up vote
1
down vote

favorite












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









New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 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 at 0:41










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










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 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 at 8:05















up vote
1
down vote

favorite









up vote
1
down vote

favorite











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









New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











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









New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited Nov 22 at 10:11





















New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked Nov 21 at 22:28









mctoothpick

62




62




New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












  • It may be helpful to see the definition of SelectFieldGroup. Is that a custom component or one from another library?
    – Tex
    Nov 21 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 at 0:41










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










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 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 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 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 at 0:41










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










  • @PranayTripathi Thanks, I edited the question!
    – mctoothpick
    Nov 22 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 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 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 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 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 at 0:41












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




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












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




@PranayTripathi Thanks, I edited the question!
– mctoothpick
Nov 22 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 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 at 8:05














1 Answer
1






active

oldest

votes

















up vote
0
down vote













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








New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.


















  • Now that looks much better - good work!
    – Tex
    Nov 23 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',
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
});


}
});






mctoothpick is a new contributor. Be nice, and check out our Code of Conduct.










 

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








up vote
0
down vote













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








New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.


















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















up vote
0
down vote













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








New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.


















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













up vote
0
down vote










up vote
0
down vote









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








New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









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








New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this answer



share|improve this answer






New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









answered Nov 23 at 17:55









mctoothpick

62




62




New contributor




mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






mctoothpick is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












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


















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
















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




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










mctoothpick is a new contributor. Be nice, and check out our Code of Conduct.










 

draft saved


draft discarded


















mctoothpick is a new contributor. Be nice, and check out our Code of Conduct.













mctoothpick is a new contributor. Be nice, and check out our Code of Conduct.












mctoothpick is a new contributor. Be nice, and check out our Code of Conduct.















 


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

What visual should I use to simply compare current year value vs last year in Power BI desktop

Alexandru Averescu

Trompette piccolo