Why useDispatch runs, but the state does not change? - javascript

Used in the project connect, but now I want to switch to hooks
When you enter input, the action is dispatched:
onChange={(e) => dispatch(actions.setInputText(e.target.value))}
but when outputting the todo element is an empty string, although when using connect everything worked fine?
const inputText = useSelector(state => state.inputText.data)
↑ This is a string with input text ↑
My project - scintillating-plantation.surge.sh
Code:
import React from "react";
import actions from "../../../actions";
import { useSelector, useDispatch } from 'react-redux'
export default function AddTodoInputBtn(props) {
const inputText = useSelector(state => state.inputText.data);
const inputActiveClass = useSelector(state => state.inputActiveClass.data);
const dispatch = useDispatch();
const inputNode = React.createRef();
function changeInputLogo() {
if (inputActiveClass === "") {
dispatch(actions.setInputActiveClass("_active"));
inputNode.current.focus();
return;
}
dispatch(actions.setInputActiveClass(""));
dispatch(actions.setInputText(""));
}
return (
<form
onSubmit={(e) => {
window.console.log(inputText); <----------------here is I show const inputText = useSelector(state => state.inputText.data);
dispatch(actions.setInputActiveClass(""));
inputNode.current.blur();
props.onAddTodo(e);
}}
className={`todo_block-add_todo add_todo_block add_todo_block${inputActiveClass}`}
>
<div
className={`add_todo_block-icon${inputActiveClass}`}
onClick={changeInputLogo}
/>
<input
ref={inputNode}
autoComplete="off"
type="text"
placeholder="Add List"
className="add_todo_block-input"
id="input"
value={inputText}
onChange={(e) => dispatch(actions.setInputText(e.target.value))}
onClick={changeInputLogo}
></input>
<button
type="submit"
className={`add_todo_block-btn add_todo_block-btn${inputActiveClass}`}
>
<div>Add</div>
</button>
</form>
);
}
reducer:
const first = {
data: "",
};
const second = {
data: false,
};
const third = {
data: JSON.parse(localStorage.getItem("todos")) || [
{ label: "Wash Kitchen", isComplete: false },
{ label: "Go To Theater", isComplete: false },
],
};
const fourth = {
data: [],
};
const fifth = {
data: [],
};
const sixth = {
data: "All",
};
const seventh = {
data: "",
};
function inputText(state = first, action) {
switch (action.type) {
case "CHANGE_INPUT_TEXT_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function isShowTips(state = second, action) {
switch (action.type) {
case "CHANGE_SHOW_TIPS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function todoList(state = third, action) {
switch (action.type) {
case "CHANGE_TODO_LIST_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function doneTodos(state = fourth, action) {
switch (action.type) {
case "CHANGE_DONE_TODOS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function undoneTodos(state = fifth, action) {
switch (action.type) {
case "CHANGE_UNDONE_TODOS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function todosType(state = sixth, action) {
switch (action.type) {
case "CHANGE_TODOS_TYPE_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function inputActiveClass(state = seventh, action) {
switch (action.type) {
case "CHANGE_INPUT_ACTIVE_CLASS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
export default {
inputText,
isShowTips,
todoList,
doneTodos,
undoneTodos,
todosType,
inputActiveClass,
};

Related

prevstate and nextstate are the same in redux

I tried to create todo list app with react and redux but when I use "CheckTodoItem" action and log with logger, my prevstate and nextstate are same.
TodoList Action :
const AddTodoItem = ( todo ) =>{
return{
type: "ADD_TODO_ITEM",
payload: todo
}
}
const CheckTodoItem = ( todo ) =>{
return{
type: "CHECK_TODO_ITEM",
payload: todo
}
}
const DeleteTodoItem = ( todo ) =>{
return{
type: "DELETE_TODO_ITEM",
payload: todo
}
}
export { AddTodoItem, CheckTodoItem, DeleteTodoItem }
TodoLIst Reducer:
const initialState = {
todoList: [
{ todo: "workout" , isCheck: false},
{ todo: "home work" , isCheck: false},
{ todo: "pay bils" , isCheck: false},
{ todo: "visit ali" , isCheck: false},
{ todo: "Buying household items" , isCheck: false},
],
}
const todoListReducer = ( state = initialState , action ) => {
switch ( action.type ) {
case "ADD_TODO_ITEM":
const Todo = {
todo: action.payload,
isCheck: false,
};
state.todoList.push( Todo );
return{
...state
}
case "DELETE_TODO_ITEM":
const newTodoList = state.todoList.filter( item => item.todo !== action.payload)
return {
...state,
todoList: newTodoList,
}
case "CHECK_TODO_ITEM":
const objIndex = state.todoList.findIndex(( obj => obj.todo === action.payload ));
state.todoList[objIndex].isCheck = true
return{
...state,
}
default :
return state
}
}
export default todoListReducer
logger :
enter image description here
I've tried everything I can but I don't know why it's not working properly
I edit your reducer function with map :
case "CHECK_TODO_ITEM":
const todoList = state.todoList.map((todoItem)=>{
if(todoItem.todo===action.payload){
return {
...todoItem,
isCheck:true,
}else{
return todoItem
}
}
});
return{
...state,
todoList
}

Redux Action Dispatches but does not map to state

I have An action That I am trying to use in my component, My FetchPosts() function will work and map to state, but when I use my other Action FetchUserPosts() I can see in the redux dev tools that the action dispatches, but in State nothing is there and the state doesn't change at all.
It could be something with my action but im not sure.
postAction.js
import { FETCH_POSTS, NEW_POSTS, FETCH_POST_FOR_APPROVAL,
FETCH_POSTS_FROM_USER } from './types';
export const fetchPostsFromUser = (id) => dispatch => {
Promise.all([fetch('http://10.6.254.22:5000/posts/1' + id)])
.then(([res1]) => {
return Promise.all([res1.json()])
})
.then(([posts]) => dispatch ({
type: FETCH_POSTS_FROM_USER,
payload: posts
}));
}
Types.js
export const FETCH_POSTS_FROM_USER = 'FETCH_POSTS_FROM_USER';
PostReducer.js
import { FETCH_POSTS, NEW_POSTS, FETCH_POST_FOR_APPROVAL,
FETCH_POSTS_FROM_USER } from '../actions/types';
const initialState = {
items: [],
item: {}
}
export default function (state = initialState, action ){
switch(action.type) {
case FETCH_POSTS:
console.log('currently reducing')
return{
...state,
items: action.payload
}
case NEW_POSTS:
return{
...state,
item: action.payload
}
case FETCH_POST_FOR_APPROVAL:
return{
...state,
items: action.payload
}
case FETCH_POSTS_FROM_USER:
return{
...state,
items: action.payload
}
default:
return state;
}
}
My Component I want the function to work with so i can map the props.
class ProfilePostBody extends Component {
constructor(props){
super(props)
this.state = {
commentBody: null,
postId: null,
giphyUrl: null,
postPicture: null,
userId: null,
userIdto: null,
userIdName: null,
userIdtoName:null,
// postBody: null,
// giphyUrl: null,
// userIdto: null,
// userIdName: null,
// userIdtoName:'Julio',
displayGifPicker: false
}
}
componentDidMount(){
this.props.fetchPostsFromUser();
this.props.fetchPosts();
}
render () {
return (
// <Grid item xl={6}>
<div>
{this.props.posts.map((post, index) =>
<PostBodyTemplate key={index} postId={post.id} onSubmit=
{this.handleSubmit} onChange={e =>
this.handleInputChange(e,post.id,post.userId,post.userIdName)} title=
{post.title} postBody={post.postBody}
giphyUrl = {post.giphyUrl} userWhoPosted={post.userIdName}
commentBody={post.commentBody} userIdtoName={post.userIdtoName}
userIdName={post.userIdName} />
)}
</div>
)
}
}
ProfilePostBody.propTypes = {
fetchPostsFromUser: PropTypes.func.isRequired,
fetchPosts: PropTypes.func.isRequired,
posts: PropTypes.array.isRequired
}
const mapStateToProps = state =>({
posts: state.posts.items
})
export default connect(mapStateToProps, { fetchPosts, fetchPostsFromUser
})
(ProfilePostBody);
fetchPosts will work, but if i comment it out and try just the fetchPostsFromUser() it will not work.

Redux state is not getting initialized to initial state

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.

Redux combineReducers, with child properties at same level as child reducers

I would like to have this shape of data:
let shop = Map({
shopInput: '',
shopShouldHideResults: true,
place: null,
position: {},
shopAutocompleteResults: {
predictions: [{ description: '' }]
}
})
let product = Map({
brand: '',
name: '',
description: '',
image: null,
price: null,
id: null,
item: 'MEAL'
})
export const addPage: Map<*, *> = Map({
shop: shop,
product: product,
shopListDisplayed: false,
rerenderKey: false
})
So addPage has a product reducer and a shop reducer and its own reducer just for its own two properties "shopListDisplayed" and "rerenderKey"
My reducers:
export const addPage = (
state: Map<*, *> = applicationState.get('addPage'),
action: AddPageAction
): Map<*, *> => {
switch (action.type) {
case UPDATE_SHOP_LIST_DISPLAYED: {
return state.set('shopListDisplayed', (action.payload: boolean))
}
case UPDATE_RERENDER_KEY: {
return state.set('rerenderKey', (!state.get('rerenderKey'): boolean))
}
default:
return state
}
}
export const product = (
state: Map<*, *> = applicationState
.get('addPage').get('product'),
action: AddPageAction
): Map<*, *> => {
switch (action.type) {
case UPDATE_BRAND: {
return state.set('brand', (action.payload: string))
}
case UPDATE_NAME: {
return state.set('name', (action.payload: string))
}
case UPDATE_ITEM_TYPE: {
return state.set('item', (action.payload: string))
}
case UPDATE_DESCRIPTION: {
return state.set('description', (action.payload: string))
}
case UPDATE_PRODUCT_ID: {
return state.set('id', (action.payload: number))
}
case UPDATE_PRICE: {
return state.set('price', (action.payload: number))
}
case UPDATE_IMAGE: {
return state.set('image', (action.payload: Image | any))
}
case UPDATE_IMAGE_LINK: {
return state.set('imageLink', (action.payload: string))
}
default:
return state
}
}
export const shop = (
state: Map<*, *> = applicationState.get('addPage').get('shop'),
action: Object
): any => {
switch (action.type) {
case UPDATE_SHOP_INPUT: {
return state.set('shopInput', action.payload.shopInput)
}
case UPDATE_SHOP_PLACE: {
return state.set('place', action.payload.place)
}
case UPDATE_SHOP_SHOULD_HIDE_RESULTS: {
return state.set('shopShouldHideResults', action.payload)
}
case GET_SHOP_AUTOCOMPLETE_RESULTS_REJECTED: {
console.log(
'there was an issue getting your autocomplete results: ',
action.payload
)
return state
}
case GET_SHOP_AUTOCOMPLETE_RESULTS_FULFILLED: {
return state.set('shopAutocompleteResults', action.payload)
}
case GET_SHOP_PLACE_DETAILS_FULFILLED: {
return state.set('place', action.payload.result)
}
case GET_SHOP_PLACE_DETAILS_REJECTED: {
console.log('there was an issue getting place details: ', action.payload)
return state
}
default:
return state
}
}
How do I use combineReducers in this case?
Currently I have:
const rootReducer = combineReducers({
addPage: combineReducers({shop, product}),
alertModal,
categories,
distanceSlider,
editProduct,
map,
menu,
searchPage: combineReducers({location}),
searchPageFields: searchPageFields,
searchResults,
searchResultsPresenter
I don't think my combineReducers takes into account shopListDisplayed: false,rerenderKey: false. I have not explicitely added in addPage reducer into combineReducers which deals with those two root properties of the addPage.
Where do I put that into combineReducers?
I get this error:
Unexpected properties "shopListDisplayed", "rerenderKey" found in the
previous state received by the reducer. Expected to find one of the
known reducer property names instead: "shop", "product". Unexpected
properties will be ignored.

Adding multiple objects to the store in redux

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

Categories

Resources