I have 8 <TouchableWithoutFeedback> elements in a grid form. They are not dynamically rendered using map.
this.props.selectCell('rain')}>
<TouchableWithoutFeedback onPress={()=>this.props.selectCell('rain')}>
<Row style={[styles.orangish,styles.cell]}>
<Image source={Rain} />
</Row>
</TouchableWithoutFeedback>
The idea is to select one onPress.
action
export const selectCell = (cellId) => {
console.log(cellId);
return {
type: 'select_cell',
payload: cellId
};
}
Reducer
export default (state= {}, action) => {
switch(action.type) {
case 'select_cell': {
//trying to figure out what to do here
}
};
1) How do I toggle the selection in reducer?
2) How to I render the a new image based on the state?
Have a selectedCellId property in the reducer state and set it as follows:
export default (state= {}, action) => {
switch(action.type) {
case 'select_cell': {
return {
...state,
selectedCellId: action.payload
};
}
};
An alternative is to have an array of 8 cells with selected = false.
const defaultState = {
cells: [
{ selected: false },
{ selected: false },
{ selected: false },
{ selected: false },
{ selected: false },
{ selected: false },
{ selected: false },
{ selected: false }
]
};
Then in the reducer,
export default (state= defaultState, action) => {
switch(action.type) {
case 'select_cell': {
let { cells } = state;
cells = cells.slice();
cells[action.payload] = { selected: true };
return {
...state,
cells
};
}
};
If you have named cells, then make the cells collection an object map instead of an array.
Related
What is the way to mark the checkbox when clicking on the BOB text?
In my example I need to check the box also by clicking on the text BOB .
I would be happy to help with this.
I add the reducer and the action of the checxbox .
hope u can understand the way of it .
this is the reducer
import {
SET_CURRENT_TAB_INFO,
SET_CURRENT_TAB_NAME,
SET_CURRENT_TIME,
SET_CHECKBOX,
SET_CHECKBOX2,
SET_REQUESTID,
SET_IS_FORM_CHECKED,
SET_INPUTS_TEXT,
} from '../../constants/BitzuaDigumConstants';
const initializeState = {
currentTabName: '',
currentTabInfo: null,
currentTime: '',
checkBox: false,
checkBox2: false,
requestId: 0,
isFormChecked: false,
inputsText: {}
};
export default function BitzuaDigumReducer(state = initializeState, action) {
switch (action.type) {
case SET_CURRENT_TAB_NAME:
return {
...state,
currentTabName: action.payload,
};
case SET_IS_FORM_CHECKED:
return {
...state,
isFormChecked: action.payload,
};
case SET_INPUTS_TEXT:
return {
...state,
inputsText: action.payload,
};
case SET_CURRENT_TAB_INFO:
if (!action.payload) {
return state;
}
if (!state.currentTabInfo) {
// here if currentTabInfo == null
return {
...state,
currentTabInfo: action.payload, // it will set the value from payload
};
}
// if currentTabInfo already had some values (it means it not null)
return {
...state,
currentTabInfo: {
...state.currentTabInfo,
...action.payload,
},
};
case SET_CURRENT_TIME:
return {
...state,
currentTime: action.payload,
};
case SET_CHECKBOX:
return {
...state,
checkBox: action.payload,
};
case SET_CHECKBOX2:
return {
...state,
checkBox2: action.payload,
};
case SET_REQUESTID:
return {
...state,
requestId: action.payload,
};
default:
return state;
}
}
this is the dispatch action
export function setCheckBox(data = false) {
const payload = data;
return { type: SET_CHECKBOX, payload };
}
import { setCheckBox } from '../redux/actions/BitzuaDigumActions';
function NetuneyDigum(props) {
const isSelected = useSelector(
(rootState) => rootState.BitzuaDigumReducer.checkBox
);
return (
<TouchableOpacity
activeOpacity={1}
style={{
flexDirection: 'row',
alignSelf: 'flex-start',
top: 20,
left: 10,
}}
>
<CheckBox
value={isSelected}
onValueChange={(value) => dispatch(setCheckBox(value))}
style={styles.checkbox}
tintColors={{ true: 'white', false: 'white' }}
/>
<Text style={styles.label}>BOB</Text>
</TouchableOpacity>
);
}
There are two ways here:
You can set the onPress property either on the TouchableOpacity component or on the Text. In React Native all Text components are touchable. And set the onPress to change the value of the checkbox similarly to what you have on onValueChange. I don't know exactly what the code would look like because I don't know what you are doing in your dispatch action.
Edit:
Example:
add this to Text:
onPress={(value) => dispatch(toggleCheckBox())}
Create this function:
export function toggleCheckBox() {
return { type: TOGGLE_CHECKBOX };
}
Add this to the reducer:
case TOGGLE_CHECKBOX:
return {
...state,
checkBox: !state.checkBox,
};
I'm dispatching an action that looks like this:
{ type: "TOGGLE_FARA", fara: true, id: "5d20d019cf42731c8f706db1" }
The "id" is merely used for identifying the correct user. The action is meant to modify the "enabled" property of my "fara" state. The fara section of my store looks like this:
{
fara: {
enabled: false, // This need to be flipped...
names: []
},
senators: {
enabled: false,
names: []
},
senateCandidates: {
enabled: false,
names: []
}
}
However, I'm not sure how to structure my reducer. I only want to change the "enabled" property. I've tried a few different ways:
export default (state = DEFAULT_STATE, action) => {
switch (action.type) {
case "INITIALIZE_SETTINGS":
return {
fara: action.fara,
senators: action.senators,
senateCandidates: action.senateCandidates,
emails: action.emails
}
case "TOGGLE_FARA":
return {
...state,
// fara['enabled']: action.fara <–– This won't compile...
// 'fara.enabled' : action.fara <–––This just gives me a key name with the string 'fara.enabled'
}
default:
return state;
}
I was going to do something like this, but this modifys the state directly which you aren't supposed to do (my redux tool extension says the states are the same, which is not ideal). Any thoughts?
export default (state = DEFAULT_STATE, action) => {
switch (action.type) {
case "INITIALIZE_SETTINGS":
return {
fara: action.fara,
senators: action.senators,
senateCandidates: action.senateCandidates,
emails: action.emails
}
case "TOGGLE_FARA":
state.fara.enabled = action.fara;
return {
...state
};
default:
return state;
}
This will work:
return {
...state,
fara: { ...state.fara, enabled: action.fara }
}
and apparently is even in the Redux documentation
I am new to the react-redux. Here, I have a reducer which is like,
const initialState = {
Low: [
{
id: 0,
technologyId: 0,
technology: '',
type: '',
count: '',
allowded: 6,
level: 'EASY'
}
],
Medium: [
{
id: 0,
technologyId: 0,
technology: '',
type: '',
count: '',
allowded: 7,
level: 'MEDIUM'
}
],
High: [
{
id: 0,
technologyId: 0,
technology: '',
type: '',
count: '',
allowded: 7,
level: 'TOUGH'
}
]
}
export default function QuizData(state = initialState, action) {
switch (action.type) {
case QUIZ_DATA:
return {
...state,
[action.data.type]: [...action.data.tobeData],
error: false,
}
case ADD_NEW:
return {
...state,
[action.data.addtype]: action.data.addData,
error: false,
}
case REMOVE_TECH:
return {
...state,
[action.data.removeType]: action.data.newArr,
error: false,
}
case RESET_QUIZ:
return {
...initialState,
error: false,
}
}
Now, Here on click of button I am calling an action that will reset the data to initial state.
this.props.resetQuiz();
which is
export function resetQuiz() {
return {
type: RESET_QUIZ
}
}
where I use it
let newData = { ...this.props.data }; while using it in the component to do some operation.
Now here what happens is after doing some actions the initial state data gets changes with some new values,..
But,on click of the button I want to set it like, initialState.
So, when I tried that time, that initialState is also getting the same values. So, It is not resetting.
I am using it in component like,
data: state.QuizData // In statetoprops.
let criterias = [{
type: 'Low',
noc: 6,
id: 1,
data: this.props.data["Low"]
}, {
type: 'Medium',
noc: 7,
id: 2,
data: this.props.data["Medium"]
},
{
type: 'High',
noc: 7,
id: 3,
data: this.props.data["High"]
}]
While using the action in component like,
createQuestionBankQuiz = () => {
this.props.resetQuiz();
history.push({
pathname: "/quiz-setup/" + `${this.props.jdId}`
});
};
export default connect(mapStateToProps, { fetchListOfQuiz, updateQuestionViewMode, enableJob, resetQuiz })(LandingScreen);
The way I update is
onChange(event, tobeupdated, id, type, noc, data) {
let newData = { ...this.props.data };
let errorState;
let isDuplicate;
let addedRow;
if (newData) {
let data = newData[type].map((object, index) => {
if (object.id === id) {
object[tobeupdated] = event.target.value;
const tobeData = newData[type];
this.props.updateLowLevel({ tobeData, type }).then(() => {
let criteria_filled = this.disableAddbutton({ ...this.props.data }, type);
addedRow = `new${type}RowAdded`;
this.setState({
[addedRow]: criteria_filled ? true : false
})
const tobechecked = newData[type].filter(item => item.id === id);
isDuplicate = this.checkPreviousSelected(newData, type, tobechecked[0].technology, tobechecked[0].type);
if (isDuplicate) {
toastr.error("Duplicate criteria. Please change it.");
object["technology"] = '';
object["type"] = '';
const tobeData = newData[type];
this.props.updateLowLevel({ tobeData, type });
}
});
}
});
errorState = `show${type}Error`;
if (tobeupdated !== "count") {
this.getSelectionQuestionsNumber(type, id, noc);
}
let validateData = this.validate(type, noc);
this.setState({
[errorState]: validateData
})
}
}
What is it that I am doing wrong ?
I think you wrong with action dispatch
export function resetQuiz() {
return dispatch => {
dispatch({
type: RESET_QUIZ
})
}
}
I think better if you try with this in reducer
export default function QuizData(state = initialState, action) {
switch (action.type) {
case RESET_QUIZ:
return Object.assign({}, initialState, {error: false})
default:
return state;
}
}
you just need to return the initial value in reducer, like this for example:
export default function QuizData(state = initialState, action) {
switch (action.type) {
case RESET_QUIZ:
return initialState
default:
return state;
}
}
You forgot to return the state in your reducer for the default action :
export default function QuizData(state = initialState, action) {
switch (action.type) {
case QUIZ_DATA:
return {
...state,
[action.data.type]: [...action.data.tobeData],
error: false,
}
case ADD_NEW:
return {
...state,
[action.data.addtype]: action.data.addData,
error: false,
}
case REMOVE_TECH:
return {
...state,
[action.data.removeType]: action.data.newArr,
error: false,
}
case RESET_QUIZ:
return {
...initialState,
error: false,
}
return state; // you forgot this
}
redux issues some kind of init action when the store is initialized, so you need to return the given state in your reducers for others actions than the ones you defined yourself.
May be you are not doing deep copy in other actions.
Can you add the code of reducer for other actions?
Edit:
Quick solution for your problems is to change your reducer like below:
export default function QuizData(state = JSON.parse(JSON.stringify(initialState)), action) {
....
}
Note: JSON.parse(JSON.stringify(initialState)) works for your case but not for all the cases, and not a good solution. You have to write deep copy logic for that.
Correct/Proper solution:
You have to modify your component logic to not modify store data directly and update it through other actions.
I have something like this on React:
const CheckboxItems = (t) => [ // that t is just a global prop
{
checked: true,
value: 'itemsCancelled',
id: 'checkBoxItemsCancelled',
labelText: t('cancellations.checkBoxItemsCancelled'),
},
{
checked: true,
value: 'requestDate',
id: 'checkboxRequestDate',
labelText: t('cancellations.checkboxRequestDate'),
},
{
checked: true,
value: 'status',
id: 'checkboxStatus',
labelText: t('cancellations.checkboxStatus'),
},
{
checked: true,
value: 'requestedBy',
id: 'checkboxRequestedBy',
labelText: t('cancellations.checkboxRequestedBy'),
},
];
class TableToolbarComp extends React.Component {
state = {
items: CheckboxItems(),
};
onChange = (value, id, event) => {
const { columnsFilterHandler } = this.props;
this.setState(({ items }) => {
const item = items.slice().find(i => i.id === id);
if (item) {
item.checked = !item.checked;
columnsFilterHandler(id, item.value, item.checked);
return { items };
}
});
};
render() {
const { items } = this.state;
return(
<>
{items.map(item => (
<ToolbarOption key={item.id}>
<Checkbox
id={item.id}
labelText={item.labelText}
value={item.value}
checked={item.checked}
onChange={this.onChange}
/>
</ToolbarOption>
))}
</>
)
}
export default compose(
connect(
({ cancellations }) => ({
columnId: cancellations.columnId,
columnValue: cancellations.columnValue,
isChecked: cancellations.isChecked,
}),
dispatch => ({
columnsFilterHandler: (columnId, columnValue, isChecked) => {
dispatch(columnsFilterAction(columnId, columnValue, isChecked));
},
}),
),
)(translate()(TableToolbarComp));
That works very well and it is dispatching the data I would need to use later.
But I have a mess on the Redux part which is changing the state of all of the checkboxes at once and not separately as it should. So, once I uncheck one of the checkboxes the other 3 also get checked: false. I don't see this change to checked: false on the UI, only I see it on the Redux console in the browser.
This is what I have in the reducer
const initialState = {
checkboxes: [
{
checked: true,
value: 'itemsCancelled',
id: 'checkBoxItemsCancelled',
},
{
checked: true,
value: 'requestDate',
id: 'checkboxRequestDate',
},
{
checked: true,
value: 'status',
id: 'checkboxStatus',
},
{
checked: true,
value: 'requestedBy',
id: 'checkboxRequestedBy',
},
],
}
[ActionTypes.COLUMNS_FILTER](state, action) {
return initialState.checkboxes.map(checkbox => {
if (!checkbox.id === action.payload.id) {
return checkbox;
}
return {
...checkbox,
checked: action.payload.isChecked,
};
});
}
Action:
const columnsFilterAction = (columnId, columnValue, isChecked) => ({
type: ActionTypes.COLUMNS_FILTER,
payload: { columnId, columnValue, isChecked },
});
So all I need to know is what I have to do manage the state of those checkboxes on Redux as it working on React. As all I see is that when I toggle the checkboxes all of them reach the same state.
You have !checkbox.id === action.payload.id as your condition logic. As all of your checkbox IDs are 'truthy', then this !checkbox.id evaluates to false, and is the same as writing if(false === action.payload.id).
I suspect you meant to write: if(checkbox.id !== action.payload.id).
What you want to do is pass the id of the checkbox you want to toggle in an action. That's all you need in an action to toggle state. Then in the reducer you want to map over the current state and just return the checkbox for any that don't match the id passed in the action. When the id does match, return a new option spreading the current checkbox's properties into the new object and setting the checked property to it's opposite.
Given this action:
const TOGGLE_CHECKBOX = 'TOGGLE_CHECKBOX'
function toggleCheckbox(id) {
return {
type: TOGGLE_CHECKBOX,
id
}
}
Actions - Redux - Guide to actions and action creators provided by the author of Redux.
This reducer will do the job.
function checkboxReducer(state = [], action = {}) {
switch(action.type) {
case TOGGLE_CHECKBOX:
return state.map(checkbox => {
if (checkbox.id !== action.id) {
return checkbox;
}
return {
...checkbox,
checked: checkbox.isChecked ? false : true,
}
})
default:
return state;
}
}
Reducers - Redux - Guide to reducers and how to handle actions provided by the author of Redux.
Here is a working Code Sandbox to demonstrate it working. You can click the checkboxes to see them toggling as expected.
I've written an API call that returns all of a user's elements in an array.
I've written part of a reducer (it doesn't fully work yet, hence the question marks below) that looks like this:
export default function(state = defaultState, action) {
switch (action.type) {
case 'receiveElement':
return {
...state,
elementsMap: {
...state.elementsMap,
[action.element.id]: action.element,
},
visibleElements: [...state.visibleElements, action.element.id],
};
case 'receiveAllElements':
return {
...state,
elementsMap: {
...state.elementsMap,
**???**
},
visibleElements: [...state.visibleElements, ...action.elements.map((element, index) =>
`id-${element.id}`
)],
};
default:
return state;
}
}
defaultState and elementsMap looks like the following:
const defaultState = {
elementsMap: {
'id-1': {id: 'id-1', shape: 'circle', title: 'Run for City Council'},
'id-2': {id: 'id-2', shape: 'circle', title: 'NYU Law School'},
'id-3': {id: 'id-3', shape: 'circle', title: 'Start Company'},
},
visibleElements: ['id-1', 'id-2', 'id-3'],
};
I'm struggling to add the additional N elements coming back from the API call to the elementsMap and would love some help here, I think I've gotten the adding to visibleElements piece down.
Thanks for taking a look
Try this:
case 'receiveAllElements':
var map = elements.reduce((res, i) => { res[i.id] = i; return res; }, {});
return {
...state,
elementsMap: {
...state.elementsMap,
...map
},
visibleElements: [...state.visibleElements, ...action.elements.map((element, index) =>
`id-${element.id}`
)],
};
export default function(state = defaultState, action) {
switch (action.type) {
case 'receiveElement':
return {
...state,
elementsMap: {
...state.elementsMap,
[action.element.id]: action.element,
},
visibleElements: [...state.visibleElements, action.element.id],
};
case 'receiveAllElements':
let _visibleElements = [...state.visibleElements, ...action.elements.map((element, index) => `id-${element.id}`)]
return {
...state,
elementsMap: {
...state.elementsMap,
**???**
},
visibleElements: _visibleElements,
};
default:
return state;
}
}
I wouldn't also do any logic on the output object, I would do it as I showed above. Otherwise, code looks messy. But that's just my opinion