React project using - javascript

I'm building UI using React, where I am using redux for increment & decrement function. Unfortunately my increment and decrement buttons are not working, may be this issue is coming due to some logical error. I will be very grateful if anyone help me to solve this issue. Here I am posting my source code.
**Creating Redux store code**
import {createStore} from 'redux';
const initialState ={counter:0, showCounter: true};
const counterReducer = (state =initialState,action) => {
if (action.type === 'increment') {
state.counter++;
return {counter: state.counter + 1,
showCounter: state.showCounter
};
}
if (action.type === 'increase') return{
counter: state.counter + action.amount,
}
if ( action.type ==='decrement'){
return {
counter: state.counter - 1,
};
}
if (action.type === 'toggle'){
return{
showCounter: !state.showCounter,
counter: state.counter
};
}
return state;
};
const store = createStore(counterReducer);
export default store;
**Counte.js code**
import {useDispatch, useSelector} from 'react-redux';
import classes from './Counter.module.css';
const Counter = () => {
const dispatch = useDispatch();
const counter = useSelector(state => state.counter);
const show = useSelector(state => state.showCounter);
const incrementHandler = () => {
dispatch({type:'incremennt', amount:10});
};
const increaseHandler = () => {
dispatch({type:'decrement'});
};
const decrementHandler = () =>{
dispatch({type:'decremennt'});
};
const toggleCounterHandler = () => {
dispatch({type:'toggle'})
};
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{show && <div className={classes.value}>{counter}</div>}
<div>
<button onClick={incrementHandler}>Increment</button>
<button onClick={increaseHandler}>Increase by 10</button>
<button onClick={decrementHandler}>Decrement</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;

I hope this code can help you:
const counterReducer = (state = initialState, action) => {
if (action.type === 'increment') {
return {
...state,
counter: state.counter + 1
};
}
if (action.type === 'increase') {
return {
...state,
counter: state.counter + action.amount
};
}
if (action.type === 'decrement') {
return {
...state,
counter: state.counter - 1
};
}
if (action.type === 'toggle') {
return {
...state,
showCounter: !state.showCounter
};
}
return state;
};
After that, you should check the typo in the type of dispatch.
decremennt and incremennt.
Try this code:
const incrementHandler = () => {
dispatch({ type: 'increment', amount: 10 });
};
const increaseHandler = () => {
dispatch({ type: 'increase' });
};
const decrementHandler = () => {
dispatch({ type: 'decrement' });
};
const toggleCounterHandler = () => {
dispatch({ type: 'toggle' });
};

Related

converting class to hooks getting messages

i'm new to react hooks, here i have been converting my project to hooks from classes, i'm getting this kind of message 'Error: Server error
at build_error (actions.js:57)
at eval (actions.js:83)' and 'GET http://127.0.0.1:8000/api/kamera/undefined 404 (Not Found)'
those errors come when i'm changing class to hooks (everything is set correcly using useState and useEffect), any idea ?
class:
initializeCollapses() {
const data = this.props[this.props.action];
let collapseStates = this.state.collapseStates;
if (!data || data.length < 1) {
return;
}
data.map((el) => {
collapseStates["" + el.name + el.identifier] = false;
return;
});
this.setState({
...this.state,
collapseStates: collapseStates,
});
}
componentDidMount() {
this.props.getItems[this.props.action](this.state.actionArgs).then(() => {
this.initializeCollapses();
});
}
Hooks:
const initializeCollapses = () => {
const data = [action];
if (!data || data.length < 1) {
return;
}
data.map((el) => {
collapseStates["" + el.name + el.identifier] = false;
return;
});
setCollapseStates(collapseStates);
};
useEffect(() => {
getItems[action](actionArgs).then(() => {
initializeCollapses();
});
}, []);
initializeCollapses() {
const data = this.props[this.props.action];
let collapseStates = this.state.collapseStates;
if (!data || data.length < 1) {
return;
}
data.map((el) => {
collapseStates["" + el.name + el.identifier] = false;
return;
});
this.setState({
...this.state,
collapseStates: collapseStates,
});
}
componentDidMount() {
this.props.getItems[this.props.action](this.state.actionArgs).then(() => {
this.initializeCollapses();
});
}
const mapDispatchToProps = (dispatch) => {
return {
getItems: {
analysers: (site) => dispatch(getAnalysers(site)),
platforms: (site) => dispatch(getPlatforms(site)),
brokers: (site) => dispatch(getBrokers(site)),
cameras: (site) => dispatch(getCameras(site)),
sites: (site) => dispatch(getSites())
},
};
};
The above class implementation in hooks would roughly be as below
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import getItems from "./store/actions";
or
import { cameras, sites, platform, brokers } from "./store/actions";
const actionArgs = useSelector(state => state.actionArgs); // In place of mapStateToProps
const dispatch = useDispatch();
useEffect(() => {
dispatch(getItems.cameras(actionArgs)) or dispatch(cameras(actionArgs)) //If destructured
}, []);
I have provided an understandable example with whatever data you provided. Refer this for a completely different approach or this one for the same mapDispatchToProps approach.
Good to refer
Example:
import React, {useReducer} from 'react';
const init = 0;
const myReducer = (state, action) => {
switch(action.type){
case 'increment':
return state + 1 // complex actions are kept in seperate files for better organised, clean code
case 'decrement':
return state - 1
case 'reset': // action types as well are kept as selectors
return init
default:
return state
}
};
function ReducerExample(){
const [count, dispatch] = useReducer(myReducer, init)
const add = () => {
dispatch({type: 'increment'})
}
const sub = () => {
dispatch({type: 'decrement'})
}
const reset = () => {
dispatch({type: 'reset'})
}
return (
<div>
<h4>Count: {count}</h4>
<button onClick={add} style={{margin: '10px'}}>Increment</button>
<button onClick={sub}>Decrement</button>
<button onClick={reset} style={{margin: '10px'}}>Reset</button>
</div>
)
}
export default ReducerExample;

Reactjs custom hook won't fire using an if/else in useEffect?

I extracted my reducer function in a custom hook. When I try to fire decreaseMinutes from the custom hook nothing happens. The other functions of the hook work great tough - such as toggleActive- (probably because they are in an event handler).
Any idea how I can solve this?
Reducer + Hook Component:
import { useReducer } from "react";
import { defaultState } from "../setDefaultState";
const DECREASE_MINUTES = "decrease minutes";
const DECREASE_SECONDS = "decrease seconds";
const TOGGLE_ISACTIVE = "toggle isActive";
const RESET = "handle reset";
export const timerReducer = (state, action) => {
switch (action.type) {
case DECREASE_SECONDS:
console.log("decrease sec works");
return {
...state,
seconds: state.seconds - 1,
};
case DECREASE_MINUTES:
return { ...state, minutes: state.minutes - 1, seconds: 59 };
case TOGGLE_ISACTIVE:
return { ...state, isActive: !state.isActive };
case RESET:
return {
...state,
seconds: action.payloads.seconds,
minutes: action.payloads.minutes,
isActive: !state.isActive,
};
default:
return state;
}
};
//extracted custom Hook
export function useTimer() {
const [timerState, dispatch] = useReducer(timerReducer, defaultState);
const decreaseSeconds = () => dispatch({ type: DECREASE_SECONDS }, console.log("decrease hook works"));
const decreaseMinutes = () => dispatch({ type: DECREASE_MINUTES });
const toggleActive = () => dispatch({ type: TOGGLE_ISACTIVE });
const reset = () =>
dispatch({
type: RESET,
payloads: {
seconds: defaultState.seconds,
minutes: defaultState.minutes,
isActive: !state.isActive,
},
});
return {
timerState,
decreaseMinutes,
decreaseSeconds,
toggleActive,
reset,
};
}
Main Component:
const Timer = () => {
const { timerState, decreaseMinutes, decreaseSeconds, toggleActive, reset } = useTimer();
const [dateState, dispatchDate] = useReducer(dateReducer, defaultState);
useEffect(() => {
let interval = null;
// reduce seconds and minutes by 1
if (timerState.isActive) {
interval = setInterval(() => {
if (timerState.seconds > 0) {
decreaseSeconds; //--> this is what I'm trying to fire
console.log("conditional works");
} else if (timerState.seconds === 0) {
if (timerState.minutes === 0) {
clearInterval(interval);
} else {
decreaseMinutes;
}
}
}, 1000);
return () => clearInterval(interval);
}
}, [timerState.isActive, timerState.seconds, timerState.minutes]);
You need to call it. Since you defined them as function. Like following:
decreaseMinutes();
decreaseSeconds();

useReducer not updating the array of items in sync, being a render late

A video with the behavior, I am clicking one time in 'A' and one time 'D', alternatively and is like there is two states, really strange!
https://www.loom.com/share/ba7a97f008b14529b15dca5396174c8c
And here is the action to update the description!
if (action.type === 'description') {
const { payload } = action;
const { description } = payload;
const objIndex = state.findIndex(obj => obj === payload.state);
state[objIndex].description = description;
return [...state];
}
And this is the big picture as requested, I tried to simplify to the code that I am testing in description input:
//outside component
const reducer = (state, action) => {
if (action.type === 'initialState') {
const { payload } = action;
console.log('state', state);
console.log('payload', payload);
return state.concat(payload);
}
if (action.type === 'description') {
const { payload } = action;
const { description } = payload;
const objIndex = state.findIndex(obj => obj === payload.state);
state[objIndex].description = description;
return [...state];
}
};
//inside component
const [states, dispatch] = useReducer(reducer, []);
function updateInitialState(value) {
dispatch({ type: 'initialState', payload: value });
}
function updateDescription(payload) {
dispatch({ type: 'description', payload });
}
useEffect(() => {
states.forEach(state => {
const descriptionInput = (state.status === undefined || state.status === 'available') && (
<FormInput
name="description"
label="Descrição"
input={
<InputText
value={state.description || ''}
onChange={({ target: { value } }) => {
const payload = { description: value, state };
updateDescription(payload);
}}
placeholder="Descrição"
/>
}
/>
);
const index = states.findIndex(e => e === state);
const updateArray = arrayInputs;
updateArray[index] = [descriptionInput];
setArrayInputs(updateArray);
});
}, [states]);

Multi step form ReactJs with Context API

I am making simple multi step form using only react and context. I tried to use react-form-hook, but as I am a new in react it is a bit difficult for to understand it.
It shows "Error: Maximum update depth exceeded." when I add the user after confirming. Thanks for helping in advance
App.js
function App() {
return (
<GlobalProvider>
<div className="container">
<UserForm />
</div>
</GlobalProvider>
);
}
GlobalState.jsx
import React, { createContext, useReducer } from 'react';
import AppReducer from './AppReducer';
const initialState = {
users: []
}
// Create context
export const GlobalContext = createContext(initialState);
// Provider component
export const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialState);
// Actions
function addUser(user) {
dispatch({
type: 'ADD_USERS',
payload: user
});
}
return (<GlobalContext.Provider value={{
users: state.users,
addUser
}}>
{children}
</GlobalContext.Provider>);
}
AppReducer.jsx
export default (state, action) => {
switch(action.type) {
case 'ADD_USERS':
return {
...state,
users: [action.payload, ...state.users]
}
default:
return state;
}
}
UserForm.jsx
const UserForm = () => {
const [step, setStep] = useState(1);
const [user, setUser] = useState({
firstname: '',
lastname: ''
})
const { firstname, lastname } = user;
const {addUser} = useContext(GlobalContext)
// Go Forward to next step
const nextStep = () => {
setStep(step + 1)
};
// Go back to prev step
const prevStep = () => {
setStep(step - 1)
};
const handleChange = input => e => {
setUser({ ...user, [e.target.name]: e.target.value })
}
if (step === 3) {
const newUser = {
id: Math.floor(Math.random() * 100000000),
firstname,
lastname
}
addUser(newUser)
setTimeout(() => {
setStep(1)
clearAll()
}, 1000);
}
const clearAll = () => {
setUser({
firstname: '',
lastname: ''
})
}
switch (step) {
case 1:
return (<UserInfo
prevStep={prevStep}
user={user}
handleChange={handleChange}
/>);
case 2:
return (<Confirm
nextStep={nextStep}
prevStep={prevStep}
user={user}
/>);
case 3:
return <Succuess />;
default:
return null
}
}
export default UserForm
Confirm.jsx
const Confirm = ({nextStep, prevStep, user}) => {
return (
<div>
<h1> Confirm</h1>
<div>
<p>{user.firstname}</p>
<p>{user.lastname}</p>
</div>
<button onClick={(e) => prevStep()} type="button" className="btn btn-primary">Back</button>
<button onClick={(e) => nextStep()} type="button" className="btn btn-success">Submit</button>
</div>
)
}
export default Confirm
I think this code should be inside a function and handled on event:
const checkFinalStep = () = {
if (step === 3) {
const newUser = {
id: Math.floor(Math.random() * 100000000),
firstname,
lastname
}
addUser(newUser)
setTimeout(() => {
setStep(1)
clearAll()
}, 1000);
}
}
Because currently it is simply inside the component, it will run every time react needs to re-render the component. And because it contains addUser reducer, it triggers updating the context which in turn renders the children, re-rendering your component again, recursively.

React useReducer: How to combine multiple reducers?

I'm not a Javascript expert so I wondered if anyone has an "elegant" way to combine multiple reducers to create a global state(Like Redux). A function that does not affect performance when a state updating multiple components etc..
Let's say I have a store.js
import React, { createContext, useReducer } from "react";
import Rootreducer from "./Rootreducer"
export const StoreContext = createContext();
const initialState = {
....
};
export const StoreProvider = props => {
const [state, dispatch] = useReducer(Rootreducer, initialState);
return (
<StoreContext.Provider value={[state, dispatch]}>
{props.children}
<StoreContext.Provider>
);
};
Rootreducer.js
import Reducer1 from "./Reducer1"
import Reducer2 from "./Reducer2"
import Reducer3 from "./Reducer3"
import Reducer4 from "./Reducer4"
const rootReducer = combineReducers({
Reducer1,
Reducer2,
Reducer3,
Reducer4
})
export default rootReducer;
Combine slice reducers (combineReducers)
The most common approach is to let each reducer manage its own property ("slice") of the state:
const combineReducers = (slices) => (state, action) =>
Object.keys(slices).reduce( // use for..in loop, if you prefer it
(acc, prop) => ({
...acc,
[prop]: slices[prop](acc[prop], action),
}),
state
);
Example:
import a from "./Reducer1";
import b from "./Reducer2";
const initialState = { a: {}, b: {} }; // some state for props a, b
const rootReducer = combineReducers({ a, b });
const StoreProvider = ({ children }) => {
const [state, dispatch] = useReducer(rootReducer, initialState);
// Important(!): memoize array value. Else all context consumers update on *every* render
const store = React.useMemo(() => [state, dispatch], [state]);
return (
<StoreContext.Provider value={store}> {children} </StoreContext.Provider>
);
};
Combine reducers in sequence
Apply multiple reducers in sequence on state with arbitrary shape, akin to reduce-reducers:
const reduceReducers = (...reducers) => (state, action) =>
reducers.reduce((acc, nextReducer) => nextReducer(acc, action), state);
Example:
const rootReducer2 = reduceReducers(a, b);
// rest like in first variant
Combine multiple useReducer Hooks
You could also combine dispatch and/or state from multiple useReducers, like:
const combineDispatch = (...dispatches) => (action) =>
dispatches.forEach((dispatch) => dispatch(action));
Example:
const [s1, d1] = useReducer(a, {}); // some init state {}
const [s2, d2] = useReducer(b, {}); // some init state {}
// don't forget to memoize again
const combinedDispatch = React.useCallback(combineDispatch(d1, d2), [d1, d2]);
const combinedState = React.useMemo(() => ({ s1, s2, }), [s1, s2]);
// This example uses separate dispatch and state contexts for better render performance
<DispatchContext.Provider value={combinedDispatch}>
<StateContext.Provider value={combinedState}> {children} </StateContext.Provider>
</DispatchContext.Provider>;
In summary
Above are the most common variants. There are also libraries like use-combined-reducers for these cases. Last, take a look at following sample combining both combineReducers and reduceReducers:
const StoreContext = React.createContext();
const initialState = { a: 1, b: 1 };
// omit distinct action types for brevity
const plusOneReducer = (state, _action) => state + 1;
const timesTwoReducer = (state, _action) => state * 2;
const rootReducer = combineReducers({
a: reduceReducers(plusOneReducer, plusOneReducer), // aNew = aOld + 1 + 1
b: reduceReducers(timesTwoReducer, plusOneReducer) // bNew = bOld * 2 + 1
});
const StoreProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(rootReducer, initialState);
const store = React.useMemo(() => [state, dispatch], [state]);
return (
<StoreContext.Provider value={store}> {children} </StoreContext.Provider>
);
};
const Comp = () => {
const [globalState, globalDispatch] = React.useContext(StoreContext);
return (
<div>
<p>
a: {globalState.a}, b: {globalState.b}
</p>
<button onClick={globalDispatch}>Click me</button>
</div>
);
};
const App = () => <StoreProvider> <Comp /> </StoreProvider>
ReactDOM.render(<App />, document.getElementById("root"));
//
// helpers
//
function combineReducers(slices) {
return (state, action) =>
Object.keys(slices).reduce(
(acc, prop) => ({
...acc,
[prop]: slices[prop](acc[prop], action)
}),
state
)
}
function reduceReducers(...reducers){
return (state, action) =>
reducers.reduce((acc, nextReducer) => nextReducer(acc, action), state)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
If you simply want to achieve a combine reducer feature without any third-party library, do it as below. (REF: Redux source/code)
The working code is here https://codepen.io/rajeshpillai/pen/jOPWYzL?editors=0010
I have two reducers created, one dateReducer and another counterReducer. I am using it as
const [state, dispatch] = useReducer(combineReducers({
counter: counterReducer,
date: dateReducer
}), initialState);
The combineReducers code
function combineReducers(reducers) {
return (state = {}, action) => {
const newState = {};
for (let key in reducers) {
newState[key] = reducers[key](state[key], action);
}
return newState;
}
}
Usage: Extract the respective state
const { counter, date } = state;
NOTE: You can add more redux like features if you wish.
The complete working code (in case codepen is down :))
const {useReducer, useEffect} = React;
function dateReducer(state, action) {
switch(action.type) {
case "set_date":
return action.payload;
break;
default:
return state;
}
}
function counterReducer(state, action) {
console.log('cr:', state);
switch (action.type) {
case 'increment': {
return state + 1;
}
case 'decrement': {
return state - 1;
}
default:
return state;
}
}
function combineReducers(reducers) {
return (state = {}, action) => {
const newState = {};
for (let key in reducers) {
newState[key] = reducers[key](state[key], action);
}
return newState;
}
}
const initialState = {
counter: 0,
date: new Date
};
function App() {
const [state, dispatch] = useReducer(combineReducers({
counter: counterReducer,
date: dateReducer
}), initialState);
console.log("state", state);
const { counter, date } = state;
return (
<div className="app">
<h3>Counter Reducer</h3>
<div className="counter">
<button onClick={() =>
dispatch({ type: 'increment'})}>+
</button>
<h2>{counter.toString()}</h2>
<button onClick={() =>
dispatch({ type: 'decrement'})}>-
</button>
</div>
<hr/>
<h3>Date Reducer</h3>
{date.toString()}
<button className="submit"
type="submit"
onClick={() =>
dispatch({ type: 'set_date', payload:new Date })}>
Set Date
</button>
</div>
);
}
const rootElement = document.querySelector("#root");
ReactDOM.render(<App />, rootElement);
NOTE: This is a quick hack (for learning and demonstration purpose only)
There is a library called react combine reducer that is specifically use for combining reducer with the context api. Below is the code sample
import { useReducer } from 'react';
import combineReducers from 'react-combine-reducers';
const initialIdentity = {
name: 'Harry'
}
const initialLocation = {
country: 'UK',
city: 'London'
}
const identityReducer = (state, action) => {
switch (action.type) {
case 'ACTION_A':
return { ...state, name: 'Puli' };
default: return state;
}
}
const locationReducer = (state, action) => {
switch (action.type) {
case 'ACTION_B':
return { ...state, city: 'Manchester' };
default: return state;
}
}
const [profileReducer, initialProfile] = combineReducers({
identity: [identityReducer, initialIdentity],
location: [locationReducer, initialLocation]
});
const [state, dispatch] = useReducer(profileReducer, initialProfile);
console.log(state);
// Outputs the following state:
// {
// identity: {
// name: "Harry"
// },
// location: {
// country: "UK",
// city: "London"
// }
// }
In your rootReducer.js file you can use combineReducers from redux to combine multiple reducers. The traditional way is:
import { combineReducers } from 'redux';
const rootReducer = combineReducers({ name: nameReducer});
export default rootReducer;
You can import the rootReducer while creating the store as:
import { combineReducers } from 'redux';
let store = createStore(rootReducer);
While using useReducer hook you can pass the rootReducer to it:
const [state, dispatch] = useReducer(rootReducer, initialState);
Hope this works for you.
Instead of using useReducer use useCombineReducers() . may change this function to accept multiple parameters based on your requirement
const inti ={ count:0, alpha:''}
export function reducer1(state, action) {
switch (action.type)
{
case 'increment':
return {...state , count: state.count + 1};
case 'decrement':
return {...state , count: state.count - 1};
default:
return {count:0};
} }
export function reducer2(state, action) {
switch (action.type) {
case 'add':
return {...state , alpha: state.alpha + action.payload };
case 'rem':
return {...state , alpha: state.alpha + action.payload};
default:
return {alpha:''};
}}
function useCombineReducers(reducer1,reducer2, init) {
const [state,setState] = useState(init);
function dispatch(action)
{
let ns = null;
if(action.type == 'add' || action.type=="rem")
{
ns = reducer2(state,action)
}
else
{
ns = reducer1(state,action)
}
setState(ns);
}
return [state, dispatch];}
function App() {
const [state,dispatch] = useCombineReducers(reducer1,reducer2,inti);
return (
<>
<Provider >
<Counter state ={state} dispatch={dispatch}></Counter>
<Alpha state ={state} dispatch={dispatch}></Alpha>
</Provider>
</>
); }
const Counter = (props) => {
return (
<div style ={{Border:'10px', width:'20px'}}>
Count : {props.state.count}
<button onClick={()=> props.dispatch({type: 'increment'})}> + </button>
<button onClick={()=> props.dispatch({type: 'decrement'})}> - </button>
</div>
)} export default Counter
const Alpha = (props) => {
return (
<div style ={{Border:'10px', width:'20px'}}>
Alpha : {props.state.alpha}
<button onClick={()=> props.dispatch({type: 'add',payload:'+'})}> + </button>
<button onClick={()=> props.dispatch({type: 'rem',payload:'-'})}> - </button>
</div>
)} export default Alpha
I played around a bit and were thinking about the problem as I had to handle it as well.
This might not be the best approach but I just defined my reducers as objects with key: reducer function combinations:
const counterRed = {
increment: (oldState, action) => ({
...oldState,
counter: oldState.counter + 1
}),
decrement: (oldState, action) => ({
...oldState,
counter: oldState.counter - 1
})
};
and
const dateRed = {
set_date: (oldState, action) => ({ ...oldState, date: action.payload })
};
and I combined them like this:
const reducer = (oldState, action) => {
const combinedReducers = { ...dateRed, ...counterRed };
let newState = null;
if (combinedReducers[action.type]) {
newState = combinedReducers[action.type](oldState, action);
}
if (newState) {
return { ...newState };
}
return oldState;
};
a working example can be seen here: https://codesandbox.io/s/jovial-kowalevski-25pzf?file=/src/App.js

Categories

Resources