Reducers is not updating the state in redux toolkit - reactjs

Want to open and close the navigation drawer by clicking on the navbar icon this is inside header component and storing the value in slice using redux toolkit that is in util.js
<img
src={NavIcon}
alt=""
className="icon nav-icon colapse-icon"
onClick={() => {
setDrawer(!drawer);
!drawer
? dispatch(drawerOpneClose(true))
: dispatch(drawerOpneClose(false));
}}
/>
Util.js slice code
import { createSlice } from "#reduxjs/toolkit";
const UtilSlice = createSlice({
name: "util",
initialState: { openDrawer: true },
reducers: {
drawerOpneClose: (state, action) => {
return {
...state,
openDrawer: action.payload,
};
},
},
});
export const UtilReducer = UtilSlice.reducer;
export const { closeNav, openNav, drawerOpneClose } = UtilSlice.actions;
Leistinig for change in data in SideBar.js
function SideBar() {
const [drawer] = useState(useSelector((state) => state.util) || false);
console.log(drawer);
return (
<div className={`${true ? "open-nav" : "close-nav"}`}>
)
}
State value is not getting updated as initially it is true it stays true if i pass false it is not getting updated

useState won't update the state value when the reducer updates.
Just use the selector value
const {openDrawer} = useSelector((state) => state.util);
return (
<div className={`${openDrawer ? "open-nav" : "close-nav"}`}>
)

Related

React component not updating using redux

I'm trying to create a very simple Project in React to learn Redux, and I'm expecting that, whenever I click a button, the text inside a paragraph will be rendered, however, I can't get the app component (the only one I have) to get updated. Where am i failing?.
import { createStore } from 'redux'
const deleteUsers = 'delete users';
const createUser = 'créate user';
const createUserStranger = 'create stranger';
function deleteUsersActionCreator() {
return {
type: deleteUsers,
data: []
}
}
function createUsersActionCreator() {
return {
type: createUser,
data: ['Irina', 'Denis', 'Julio']
}
}
function createUserStrangerActionCreator() {
return {
type: createUserStranger,
data: 'stranger'
}
}
const initialState = {
users: []
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case createUser:
return {
...state,
users: [...state.users, ...action.data]
}
case deleteUsers:
return {
...state,
users: []
}
case createUserStranger:
return {
...state,
users: [...state.users, action.data]
}
default:
return state
}
};
function dispatchAction(action) {
store.dispatch(action)
}
const store = createStore(reducer);
let users = [];
store.subscribe(() => {
users = store.getState().users
console.log('users', users)
});
const App = () => {
console.log(store.getState().users.map(e => e));
console.log('rendering again')
return (
<div className="App">
<h1>People App </h1>
<hr/>
<p>
{
store.getState().users.map( user => {
return
(<p>{user}</p>)
})
}
</p>
<button onClick={()=> dispatchAction(createUsersActionCreator())}>Add Family</button>
<button onClick={()=> dispatchAction(createUserStrangerActionCreator())}>Add Stranger</button>
<button onClick={()=> dispatchAction(deleteUsersActionCreator())}>Delete users</button>
</div>
)
}
export default App
I know I should be using useState, usteEffect and this approaches, but since I'm learning react from zero, I thought it was a Good idea to learn redux as well. I've also Heard about Zustand (which I'm gonna learn too) but I'd like to have a Good grasp of raw redux. Any advise is very Good welcome :)
Since you are using React - better to use Hooks useSelector and useDispatch inside components
const App = () => {
const users = useSelector(state => state.users)
const dispatch = useDispatch()
console.log('rendering again', users)
return (
<div className="App">
<h1>People App </h1>
<hr/>
<p>
{
users.map( user => {
return
(<p>{user}</p>)
})
}
</p>
<button onClick={()=> dispatch(createUsersActionCreator())}>Add Family</button>
<button onClick={()=> dispatch(createUserStrangerActionCreator())}>Add Stranger</button>
<button onClick={()=> dispatch(deleteUsersActionCreator())}>Delete users</button>
</div>
)
}
export default App

How to Revert to the Original Background Color of previously Clicked Components When Clicking Another in React

What I am trying to achieve is, as mentioned in the title, to revert the component's background color when another entry component in the sidebar gets clicked. I use React Context API for state management. The initial state contains an array of objects named numbers which has two elements (id & number) and current. On Sidebar.js, it renders the SidebarEntry component iterating the numbers array.
I know why I am stuck at this point. It is because there is no way that I can change the state of the previously clicked component unless it gets clicked again. The following code snippet is what I reproduced my issue.
reducer.js
export const initialState = {
numbers: [
{
id: 1,
number: 101
},
{
id: 2,
number: 102
},
{
id: 3,
number: 103
},
],
current: null
}
const reducer = (state, action) => {
switch(action.type) {
case 'CHANGE_STATE':
return {
...state,
current: action.current
};
default:
return state;
}
}
export default reducer;
StateProvider.js (This wraps in index.js with initialState and reducer arguments)
import React, { createContext, useContext, useReducer } from "react";
export const StateContext = createContext();
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
Sidebar.js
const Sidebar = () => {
const [{ numbers }] = useStateValue();
return (
<div className="sidebar">
{
numbers.map(number => (
<SidebarEntry key={number.id} number = {number.number} />
))
}
</div>
)
}
export default Sidebar
SidebarEntry.js
const SidebarEntry = ({ number }) => {
const [{ current }, dispatch] = useStateValue();
const [selected, setSelected] = useState(false);
const changeState = (e) => {
e.preventDefault();
dispatch({
type: "CHANGE_STATE",
current: {number}
});
setSelected(!selected);
}
return (
<div className="sidebarEntry">
<h3
className={selected && "sidebarEntry__color"}
onClick={changeState}
>
{number}
</h3>
</div>
)
}
export default SidebarEntry
Sidebar & SidebarEntry
When clicking 101, then clicking 102 (101's background color is supposed to be back to gray)
The problem is that when you dispatch a new active value in redux, you are actually changing the redux state.
But you are not changing the current state of the component selected.
I would delete this state altogether and do it like this:
const SidebarEntry = ({ number }) => {
const [{ current }, dispatch] = useStateValue();
const changeState = (e) => {
e.preventDefault();
dispatch({
type: "CHANGE_STATE",
current: {number}
});
setSelected(!selected);
}
return (
<div className="sidebarEntry">
<h3
className={current === number && "sidebarEntry__color"}
onClick={changeState}
>
{number}
</h3>
</div>
)
}
export default SidebarEntry
This should work for you

React dispatch from reducer firing twice

Whenever I call a function from the reducer, it gets called once the first time, and then twice every other time.
Here's the code:
reducer.js:
import data from './data'
export const initialState = {
notes: data,
filter: '',
};
export const setFilter = filter => ({ type: 'setFilter', filter });
export const createNote = id => ({ type: 'createNote', id })
export const deleteNote = note => ({ type: 'deleteNote', note })
export const reducer = (state, action) => {
switch (action.type) {
case 'setFilter':
return { ...state, filter: action.filter };
case 'createNote':
console.count('Create note fired')
state.notes.push({
id: action.id,
tags: [],
content: ""
})
return { ...state }
case 'deleteNote':
return {
...state,
notes: state.notes.filter((note) => note.id !== action.note.id)
}
default: return state;
}
};
The component that calls the delete method:
import React from 'react'
import PropTypes from 'prop-types';
import { deleteNote } from "../../state/reducer";
import { useStateValue } from "../../state/StateContext";
import './Body.css'
import { Card, Badge } from 'react-bootstrap'
const Body = ({ notes }) => {
let [state, dispatch] = useStateValue();
return (
<div className="Body">
{
notes.map(note =>
<Card key={note.id} className="Card">
<Card.Body className="CardText HideScrollbar">
<Card.Text>{note.content}</Card.Text>
</Card.Body>
<Card.Footer>
{note.tags.map(tag =>
<Badge variant="primary">
{tag} </Badge>)}
</Card.Footer>
<div className="DeleteButton" onClick={() => dispatch(deleteNote(note))}>
<svg className="svg-icon" viewBox="0 0 20 20">
<path d="M10.185,1.417c-4.741,0-8.583,3.842-8.583,8.583c0,4.74,3.842,8.582,8.583,8.582S18.768,14.74,18.768,10C18.768,5.259,14.926,1.417,10.185,1.417 M10.185,17.68c-4.235,0-7.679-3.445-7.679-7.68c0-4.235,3.444-7.679,7.679-7.679S17.864,5.765,17.864,10C17.864,14.234,14.42,17.68,10.185,17.68 M10.824,10l2.842-2.844c0.178-0.176,0.178-0.46,0-0.637c-0.177-0.178-0.461-0.178-0.637,0l-2.844,2.841L7.341,6.52c-0.176-0.178-0.46-0.178-0.637,0c-0.178,0.176-0.178,0.461,0,0.637L9.546,10l-2.841,2.844c-0.178,0.176-0.178,0.461,0,0.637c0.178,0.178,0.459,0.178,0.637,0l2.844-2.841l2.844,2.841c0.178,0.178,0.459,0.178,0.637,0c0.178-0.176,0.178-0.461,0-0.637L10.824,10z"></path>
</svg>
</div>
</Card>
)
}
</div>
)
}
Body.propTypes = {
notes: PropTypes.arrayOf(PropTypes.object),
}
export default Body
Any kind of help would be really helpful, please tell me if there's any file missing or if I implemented the reducer in the wrong way, what I did was mostly following notes from a friend's University professor
make seperate action file. And get that action from redux through mapDispatchToProps in your component , where you want to dispatch that action.
const mapDispatchToProps = {
setProfileDialog: ProfileAction.setProfileDialog,
}
The issue is that reducers must be pure. When react is in 'strict-mode' it will fire reducers twice to ensure that the result is the same both times. Mutating the original state will cause unwanted side effects.
Changing:
case 'createNote':
console.count('Create note fired')
state.notes.push({
id: action.id,
tags: [],
content: ""
})
return { ...state }
To:
case 'createNote':
const notes = [
...state.notes,
{
id: action.id,
tags: [],
content: "",
}
]
return {...state, notes}
Should fix your example.

updating state through dispatch function

I am building a to do list. One of the features is to be able to add tasks to the do list via user input. I am holding an initial list of todos in state in a reducer pattern. There is an action called ADD_ITEM that should add a new task to the todo list. However, this dispatch function does not appear to be working. When I click on the button that should add a new task to my todo list, it only adds a blank <li> to the list. When I try to use the user input to add a new todo and console.log newItemText which should be set by the input, it gets logged as undefined. Can someone take a look at the code and tell me why this is happening?
TodoList.js:
import React, { useState, useReducer } from"react";
import Todo from "./Todo";
import { initialState, reducer } from "../reducers/todoReducer";
import { ADD_ITEM } from "../actions/actions";
const TodoList = props => {
const [state, dispatch] = useReducer(reducer, initialState);
const [newItemText, setNewItemText] = useState("");
const handleChanges = e => {
console.log(e.target.value)
setNewItemText(e.target.vaue);
};
console.log(newItemText);
return (
<div>
<ul className="task-list">
{state.map(task => (
<Todo key={task.item} task={task} />
))}
</ul>
<input
className="add-input"
name="todo"
type="text"
placeholder="Enter a task"
value={newItemText}
onChange={handleChanges}
/>
<button
className="add-button"
onClick={() => dispatch({ type: ADD_ITEM, payload: newItemText })}
>
Add a Task
</button>
<button
className="add-button"
onClick={null}
>
Remove Completed
</button>
</div>
)
}
export default TodoList;
todoReducer.js:
import { ADD_ITEM, TOGGLE_COMPLETED, REMOVE_COMPLETED } from "../actions/actions";
export const initialState = [
{ item: 'Learn about context API', completed: false, id: 1},
{ item: 'Learn about reducers', completed: false, id: 2},
{ item: 'complete context API project', completed: false, id: 3},
{ item: 'complete reducers project', completed: false, id: 4}
];
export const reducer = (state = initialState, action) => {
console.log(action)
switch(action.type) {
case ADD_ITEM:
return [
...state,
{
item: action.payload,
completed: false,
id: Date.now()
}
]
case TOGGLE_COMPLETED:
const toggledState = [...state];
toggledState.map(item => {
if(item.id === action.payload) {
item.completed = !item.completed;
}
})
state = toggledState;
return state;
case REMOVE_COMPLETED:
return state.filter(item => !item.completed);
default:
return state;
}
}
Inside handleChanges function, you misspelt value:
setNewItemText(e.target.vaue);
;)

UIkit's modals in React: integration

I'm working on this project where the frontend is in React with UIkit for the user interface. The integration between the parts looks poorly implemented. I'm going to explain why. There is a Modal component, something like
export class Modal extends Component {
static getByName = name => UIkit.modal(`[data-modal-name='${name}']`)
static show = name => {
const modal = Modal.getByName(name)
if (modal) modal.show()
}
static hide = name => {
const modal = Modal.getByName(name)
if (modal) modal.hide()
}
render() {
// a modal
}
}
this is used in this way
export const LoginFormModal = props => (
<Modal name="login-form" className="login-form-modal" hideClose>
<LoginForm />
</Modal>
)
and show/hide is called programmatically where needed (even redux's actions)
Modal.hide("login-form")
this is in a Redux action, like this
export const login = credentials => {
return dispatch => {
dispatch(showLoader())
API.authentication.login(
credentials,
response => {
setCurrentUser(
Object.assign({}, response.user, { user_id: response.user.id })
)
Modal.hide("login-form")
dispatch(loginSucceded(response))
dispatch(hideLoader())
dispatch(push("/"))
dispatch(fetchNotificationsCounter())
},
error => {
dispatch(loginFailed(error))
dispatch(hideLoader())
}
)
}
}
This seems to work. Until you leave a component. When you come back to it, the second time the programmatically hide does not work anymore.
Anyone can lead me to how integrate the parts in a more react-appropriate way?
Using the parts of uikit which manipulate the dom (show, hide) is obviously hard to connect with React (and probably you shouldn't), however:
You need to move the call of the functions show and hide inside the Component by passing the bool of the state of the modal (eg. modalopen) . A good hook is the componentWillReceiveProps which can be used to check the previus props
componentWillReceiveProps(nextProps) {
if (nextProps.modalopen !== this.props.modalopen) {
if (nextProps.modalopen) {
getByName(...).show()
} else {
getByName(...).hide()
}
}
}
(this is inside the Modal class)
The thing I don't like and that is definitely not a "React-way" is that the code is mutating state directly from an action creator (!). From React docs:
For example, instead of exposing open() and close() methods on a
Dialog component, pass an isOpen prop to it.
So what if you had one modal that would be controlled by the redux state? Here is a possible implementation:
ModalWindow - will react to state changes and render depending what's in store:
import React from 'react';
import InfoContent from './InfoContent';
import YesOrNoContent from './YesOrNoContent';
import { MODAL_ACTION } from './modal/reducer';
class ModalWindow extends React.Component {
renderModalTitle = () => {
switch (this.props.modalAction) {
case MODAL_ACTION.INFO:
return 'Info';
case MODAL_ACTION.YES_OR_NO:
return 'Are you sure?';
default:
return '';
}
};
renderModalContent = () => {
switch (this.props.modalAction) {
case MODAL_ACTION.INFO:
return <InfoContent />;
case MODAL_ACTION.YES_OR_NO:
return <YesOrNoContent />;
default:
return null;
}
};
render() {
return (
this.props.isModalVisible ?
<div>
<p>{this.renderTitle()}</p>
<div>
{this.renderModalContent()}
</div>
</div>
:
null
);
}
}
export default connect((state) => ({
modalAction: state.modal.modalAction,
isModalVisible: state.modal.isModalVisible,
}))(ModalWindow);
modal reducer it will expose API to show/hide modal window in the application:
export const SHOW_MODAL = 'SHOW_MODAL';
export const HIDE_MODAL = 'HIDE_MODAL';
const INITIAL_STATE = {
isModalVisible: false,
modalAction: '',
};
export default function reducer(state = INITIAL_STATE, action) {
switch (action.type) {
case SHOW_MODAL:
return { ...state, isModalVisible: true, modalAction: action.modalAction };
case HIDE_MODAL:
return { ...state, isModalVisible: false };
default:
return state;
}
}
export const MODAL_ACTION = {
YES_OR_NO: 'YES_OR_NO',
INFO: 'INFO',
};
const showModal = (modalAction) => ({ type: SHOW_MODAL, modalAction });
export const hideModal = () => ({ type: HIDE_MODAL });
export const showInformation = () => showModal(MODAL_ACTION.INFO);
export const askForConfirmation = () => showModal(MODAL_ACTION.YES_OR_NO);
So basically you expose simple API in form of redux action-creators to control the state of your ModalWindow. Which you can later use like:
dispatch(showInformation())
...
dispatch(hideModal())
Of course, there could be more to it like optional configuration that would be passed to action creators or queue for modals.
I use a combination of a hook and a component for this.
Hook:
import { useState } from "react";
import UIkit from "uikit";
export default function useModal() {
const [isOpen, setIsOpen] = useState(false);
const [ref, setRef] = useState(null);
const open = (e) => {
UIkit.modal(ref).show();
setIsOpen(true);
};
const close = (e) => {
UIkit.modal(ref).hide();
UIkit.modal(ref).$destroy(true);
setIsOpen(false);
};
return [setRef, isOpen, open, close];
}
Component:
import React, { forwardRef } from "react";
const Modal = forwardRef(({ children, isOpen, full, close }, ref) => (
<div
ref={ref}
data-uk-modal="container: #root; stack: true; esc-close: false; bg-close: false"
className={`uk-flex-top ${full ? "uk-modal-container" : ""}`}
>
<div className="uk-modal-dialog uk-margin-auto-vertical">
<button
type="button"
className="uk-modal-close-default"
data-uk-icon="close"
onClick={close}
/>
{isOpen && children()}
</div>
</div>
));
export default Modal;
Consumption:
function Demo() {
const [ref, isOpen, open, close] = useModal();
return (
<div>
<button
type="button"
className="uk-button uk-button-default"
onClick={open}
>
upgrade
</button>
<Modal isOpen={isOpen} close={close} ref={ref} full>
{() => (
<div>
<div className="uk-modal-header">
<h2 className="uk-modal-title">title</h2>
</div>
<div className="uk-modal-body">
body
</div>
</div>
)}
</Modal>
</div>
);
}
Read more: https://reactjs.org/docs/integrating-with-other-libraries.html

Resources