I'm using React and Redux to try to increase the value of a prop everytime a button is clicked. However the prop is being treated as a NaN for some reason.
This is my reducer:
export default function(){
return 0;
}
And the other reducer:
export default function (state = null, action) {
switch (action.type) {
case 'VALUE_INCREMENTED':
return action.payload;
break;
}
return state;
}
This is my action:
export const incrementValue = (val) =>{
console.log("You incrementend the value to: ", val+1);
return {
type: 'VALUE_INCREMENTED',
payload: val+1
}
};
And this is is the container:
class Thermo extends Component{
getValue(){
return this.props.val;
}
render(){
return(
<div>
<Thermometer
min={0}
max={90}
width={10}
height={230}
backgroundColor={'gray'}
fillColor={'pink'}
current={this.props.val}
/>
<input type = "submit" onClick={() => this.props.incrementValue(this.props.val)}value="+"/>
</div>
)
}
}
function mapStateToProps(state){
return{
val: state.val
};
}
function matchDispatchToProps(dispatch){
return bindActionCreators({incrementValue: incrementValue}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Thermo);
Here you are setting initial state to null;
export default function (state = null, action) {
switch (action.type) {
case 'VALUE_INCREMENTED':
return action.payload;
break;
}
return state;
}
if you change your reducer to the following you will then have a default state object with a key for val and value of 0. Then when you map state to props it will get the correct value and not undefined.
var initialState = {
val: 0
}
export default function (state = initialState, action) {
switch (action.type) {
case 'VALUE_INCREMENTED':
return {...state, val: action.payload};
break;
}
return state;
}
You need an initial state in your reducer e.g.
export default function(state=0){
return state;
}
Related
I have two actions one that will close my menu (would be false) and one that will open the menu (would be true) in my reducer I set the initial value to true
import { OPENED_MENU,CLOSED_MENU } from './types';
export const OpenMenu = status => ({
type:OPENED_MENU,
status
});
export const CloseMenu = status => ({
type:CLOSED_MENU,
status
});
reducer:
import { OPENED_MENU, CLOSED_MENU } from '../../actions/menu/types';
const initialState = {
status: true,
};
const CheckingStatus = ( state = initialState, action) => {
switch (action.type){
case OPENED_MENU:
return{
}
case CLOSED_MENU:
return{
}
default:
return state;
}
}
export default CheckingStatus;
I would like to know how I return in my action a false or true boolean value.
or how it could improve my logic.
You can try the following code:
import {TOGGLE_MENU } from '../../actions/menu/types';
const initialState = {
menuStatus: true,
};
const CheckingStatus = ( state = initialState, action) => {
switch (action.type){
case TOGGLE_MENU:
return{
...state,
menuStatus: !state.menuStatus
}
default:
return state;
}
}
export default CheckingStatus;
Why won't you toggle the menu value, you just create one action and use it to open and close.
On the reducer you toggle the prevState
I have these Reducers:
const initialState = {
categories: [],
programms: {}
}
export const programmReducers = (state = initialState, action) => {
let programms = state.programms;
switch (action.type) {
case actionTypes.FETCH_CATEGORIES:
return Object.assign({}, state, {
categories: action.payload
})
case actionTypes.FETCH_PROGRAMM:
programms[action.payload.id] = action.payload;
console.log(programms);
return {
...state,
programms: Object.assign({}, programms)
}
case actionTypes.FETCH_PROGRAMM_COMPONENTS:
programms[action.programmId].components = action.payload;
console.log('Added Components')
return {
...state,
programms: programms
}
default:
return state
}
}
The last one (FETCH_PROGRAMM_COMPONENTS) adds an array to an object in the programm object. This works but somehow it won't fire componentDidUpdate in my component. It works for FETCH_PROGRAMM though.
class ProgrammPage extends Component {
static async getInitialProps({ store, query: {id} }) {
let programm;
if (!store.getState().programm.programms[id]) {
console.log('Programm not! found');
programm = await store.dispatch(loadProgramm(id));
await store.dispatch(loadProgrammComponents(id));
} else {
programm = store.getState().programm.programms[id];
console.log('Programm found')
}
return {
// programm: programm
programmId: id
}
}
componentDidUpdate(prevProps) {
console.log('UPDATE', this.props, this.props.programm.components.length)
if (!prevProps.user && this.props.user) {
this.props.loadProgrammComponents(this.props.programmId);
}
}
render() {
return (
<div>
<h1>Programm</h1>
<h2>{this.props.programm.name}</h2>
<h2>{this.props.programm.id}</h2>
<h3>Components: {this.props.programm.components ? this.props.programm.components.length : 'None'}</h3>
<br></br>
<h1>User: { this.props.user ? this.props.user.uid : 'None'}</h1>
<button onClick={() => this.props.loadProgramm('ProgrammLevel2')}>Load Programm</button>
<button onClick={() => this.props.loadProgrammComponents(this.props.programmId)}>Load Components</button>
</div>
)
}
}
function mapStateToProps(state) {
return {
programm: state.programm.programms['ProgrammLevel1'],
programms: state.programm.programms,
user: state.auth.user
}
}
const mapDispatchToProps = dispatch => bindActionCreators({
loadProgrammComponents,
loadProgramm
}, dispatch)
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProgrammPage)
You returning the same reference.
Try returning a copy of programms array: [...programms] ( or Object.assign() if it's an Object).
case actionTypes.FETCH_PROGRAMM_COMPONENTS:
programms[action.programmId].components = action.payload;
console.log('Added Components')
return {
...state,
programms: [...programms] // <-- Return new state
}
reducers/counter.js
export type counterStateType = {
+ctr: number,
+counter: boolean
};
type actionType = {
+type: string,
+value: any
};
export default function counter(state: counterStateType = { ctr: 0, counter: true}, action: actionType) {
console.log("Reducer called with");
console.log(state);//has valid value ie { ctr: 0, counter: true}
switch (action.type) {
case TOGGLE:
state.counter = !state.counter;
return state;
case UPDATE:
state.ctr = action.value;
return state;
default:
return state;
}
}
counterPage.js
function mapStateToProps(state) {
console.log("mapStateToProps called with");
console.log(state.counter);
return {
ctr: state.counter.ctr,//<= undefined
counter: state.counter.counter
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(CounterActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
PS: The above is on router LOCATION_CHANGE
The issue was in reducer, I had forgotten about immutablity of redux states. modifying,
switch (action.type) {
case TOGGLE:
state.counter = !state.counter;
return state;
case UPDATE:
state.ctr = action.value;
return state;
default:
return state;
}
to
switch (action.type) {
case TOGGLE:
return {ctr: state.ctr, counter: !state.counter};
case UPDATE:
return {ctr: action.value, counter: state.counter};
default:
return state;
}
solved it.
When triggering an action updateLog, it seems it resets other state items. In my case updateLog should manipulate log and that works just fine. The thing is it also resets tasks to the default values. What am I doing wrong here?
Component:
class Generator extends Component {
render() {
return (
<div className="generator">
<Inputs />
<button onClick={this.generate.bind(this)}>Go!</button>
<Log />
</div>
);
}
generate() {
this.props.updateLog("ANYTHING!");
}
}
function mapStateToProps(state) {
return {
tasks: state.tasks
};
}
function matchDispatchToProps(dispatch){
return bindActionCreators({updateLog: updateLog}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Generator);
Action:
export const updateLog = (message) => {
return {
type: 'LOG_UPDATED',
payload: message
}
};
Logreducer:
const initialLog = "";
export default function (state = initialLog, action) {
switch (action.type) {
case 'LOG_UPDATED':
return state + "\n" + action.payload
break;
}
return state;
}
All reducers:
const allReducers = combineReducers({
tasks: taskReducer,
log: logReducer
});
export default allReducers
taskReducer:
export default function (state = null, action) {
switch (action.type) {
case 'TASK_UPDATED':
var tasks = Object.assign({}, action.payload);
return tasks;
break;
}
// Default task properties
return {
CreateDatabaseTask: {
enabled: false,
type: "sqlite"
}
}
}
The problem lies in your task reducer. If the action type matches none of the ones defined in the switch statement, you should return the current state. Instead, you are returning the initial state.
Try changing it to return the current state instead:
const initialState = {
CreateDatabaseTask: {
enabled: false,
type: "sqlite"
}
}
export default function (state = initialState, action) {
switch (action.type) {
case 'TASK_UPDATED':
var tasks = Object.assign({}, action.payload);
return tasks;
break;
default:
return state;
}
}
I have a table with check boxes. When a user selects a check box, the id is sent to an action creator.
Component.js
handlePersonSelect({ id }, event) {
const { selectPerson, deselectPerson } = this.props;
event.target.checked ? selectPerson(id) : deselectPerson(id);
}
action.js
export function selectPerson(id) {
console.log("selected")
return {
type: SELECT_PERSON,
payload: id
};
}
export function deselectPerson(id) {
return {
type: DESELECT_PERSON,
payload: id
};
}
I'm able to go all the way up to this step, at which I'm lost:
This is the offending code:
import {
SELECT_PERSON,
DESELECT_PERSON,
FETCH_PEOPLE
} from '../actions/types';
const INITIAL_STATE = { people:[], selectedPeople:[]};
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_PEOPLE:
return {...state, people: action.payload.data};
case SELECT_PERSON:
return [action.payload, ...state ];
case DESELECT_PERSON:
return _.without({state, selectedPeople:action.payload});
}
return state;
}
The selectedPeople state should add/subtract the id's based on each case. As of now, everytime I deselect the id, my whole "people" table disappears.
You should return the complete state from the reducer in all cases, so:
export default function(state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_PEOPLE:
return {...state, people: action.payload.data};
case SELECT_PERSON:
return {...state, selectedPeople: [ ...state.selectedPeople, action.payload]};
case DESELECT_PERSON:
return {...state, selectedPeople: _.without(state.selectedPeople, action.payload)};
default
return state;
}
}
Also note you didn't have a DESELECT_PERSON action, but instead 2 SELECT_PERSON actions. Possibly a typo.