Why my redux state is null but in console everything is working - javascript

I'm trying to make todo app but,
Every time I input something, my redux developer tools shows an '' (empty string)
my reduce look like this
const TodoReducer = (state = initialState, action) => {
switch(action.type){
case 'ADD_TODO':
return {
...state,
todoList: [...state.todoList, action.item]
}
}
}
export default TodoReducer;
my action
export const addTodo = () => {
return {
type: 'ADD_TODO',
item: ''
}
}
and the App.js
function App() {
const [input, setInput] = useState('');
const todoList = useSelector(state => state.todoList)
const dispatch = useDispatch();
const addHandler = () => {
console.log(`adding ${input}`)
dispatch(addTodo({
item: input
}))
setInput('');
}
return (
<div>
<p>TODO</p>
<p>{todoList}</p>
<input type="text"
value={input}
onChange={e=> setInput(e.target.value)}
/>
<button type="button" onClick={addHandler}>Add</button>
</div>
);
}
thank you in advance. any help is appreciated

You forgot to pass the item as an input to the action and return
Change:
export const addTodo = () => {
return {
type: 'ADD_TODO',
item: ''
}
}
To:
export const addTodo = (item) => {
return {
type: 'ADD_TODO',
item: item
}
}
And
Change
<p>{todoList}</p>
To
<p>
{Array.isArray(todoList) &&
todoList.map((item, itemIndex) => (
<div key={itemIndex}>{item}</div>
))}
</p>

Related

dispatch is not a function at onClick in reactjs

I want to write context with UseReducer hook but an error
error this:dispatch is not a function
what is problem?
please help me guys
almost is correct but not working it.
I want to see the appropriate action by clicking on the buttons,
one is increment, one is decrement and the other is reset.
CounterOne
import { UseCount, UseCountActions } from "./CounterProvider";
const CounterOne = () => {
const count = UseCount();
const dispatch = UseCountActions();
return (
<div>
<h2>count is:{count}</h2>
<button onClick={() => dispatch({ type: "add", value: 1 })}>
Addone{" "}
</button>
<button onClick={() => dispatch({ type: "decrement", value: 1 })}>
decrement
</button>
<button onClick={() => dispatch({ type: "reset" })}>reset</button>
</div>
);
};
export default CounterOne;
CounterProvider
import React, { useReducer, useState } from "react";
import { useContext } from "react";
const CounterContext = React.createContext();
const CounterContextDispather = React.createContext();
const initialState = 0;
const reducer = (state, action) => {
switch (action.type) {
case "add":
return state + action.value;
case "decrement":
return state - action.value;
case "reset":
return initialState;
default:
return state;
}
};
const CounterProvider = ({ children }) => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<CounterContext.Provider value={count}>
<CounterContextDispather.Provider value={dispatch}>
{children}
</CounterContextDispather.Provider>
</CounterContext.Provider>
);
};
export default CounterProvider;
export const UseCount = () => useContext(CounterContext);
export const UseCountActions = () => {
return CounterContextDispather;
};
export const UseCountActions = () => {
return useContext(CounterContextDispather);
};
There is a official example

TypeError: updateElement is not a function

I am trying to update an element from an array by adding an object as a property like shown in this picture
When a user clicks on a single node button, a modal appears the user fills the form and then it is addes as a property for this node.
But for some reason I get this type error that says that the updateElement is not a function.
BTW, I am using Redux & react-flow-renderer libraries.
Reducer
import * as types from '../actions/types';
const initialState = {
elements: []
};
const flow = (state = initialState, action) => {
switch (action.type) {
case types.UPDATE_ELEMENT:
return {
...state,
elements: state.elements.map((e) => {
if (e.id === action.payload.id) {
e = {
...e,
options: action.payload.options,
};
}
return e;
}),
};
default:
return state;
}
};
export default flow;
Action
import { UPDATE_ELEMENT } from './types';
export const updateElement = (data) => (dispatch) => {
dispatch({
type: UPDATE_ELEMENT,
payload: data,
});
};
Node modal
import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { updateElement } from '../../../../redux/actions/flow';
const VPCNodeModal = (props, { updateElement }) => {
const [formData, setFormData] = useState({
instance: '',
});
// options
const { instance } = formData;
const onFormChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmitForm = () => {
const update = {
id: selectedElement.id,
options: formData,
};
updateElement(update);
};
return (
<>
<Modal {...props}>
<form
onSubmit={(e) => {
e.preventDefault();
onSubmitForm();
}}
>
<label>
<span> Instance name:</span>
<input
type='text'
name='instance'
value={instance}
onChange={onFormChange}
/>
</label>
<button type='submit'>Submit</button>
</form>
</Modal>
</>
);
};
VPCNodeModal.propTypes = {
updateElement: PropTypes.func.isRequired
};
export default connect(null, { updateElement })(VPCNodeModal);
Issue is while receiving the props.
change
const VPCNodeModal = (props, { updateElement }) => {
to
const VPCNodeModal = (props) => {
const { updateElement } = props;
updateElement is a props was passes in VPCNodeModal. So you should update like this with spread operator
const VPCNodeModal = ({ updateElement, ...props }) => {

DIspatch not firing in React Redux

I am trying to build a search bar component that will later make an api call and store the results in redux. however I am having trouble getting the onChange method working properly. My text onscreen doesn't change, but I can see in the console that it keeps printing out the initial state plus the last letter I entered. I did some console logs but I can't seem to get my searchReducer to run at all.
// Searchbar.js
const Searchbar = ({ query, results }) => {
const onSubmit = (e) => {
e.preventDefault();
};
const onChange = (e) => {
UpdateQuery(e.target.value);
};
return (
<form onSubmit={onSubmit}>
<label htmlFor="search">Search Bar</label>
<input
className="search-input"
name="search"
type="text"
placeholder="Search Meals..."
value={query}
onChange={onChange}
/>
<input className="search-btn" type="submit" value="Search" />
</form>
);
};
const mapStateToProps = (state) => {
return {
query: state.search.query,
results: state.search.results,
};
};
const mapDispatchToProps = (dispatch) => {
return {
UpdateQuery: (query) => dispatch(UpdateQuery(query)),
UpdateResults: (results) => dispatch(UpdateResults(results)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Searchbar);
// search-actions.js
import * as actionTypes from "./search-types";
export const UpdateQuery = (query) => {
console.log("query >>> " + query);
return {
type: actionTypes.UPDATE_QUERY,
payload: query,
};
};
export const UpdateResults = (results) => {
console.log("results >>> " + results);
return {
type: actionTypes.UPDATE_RESULTS,
payload: results,
};
};
// search-reducer.js
import * as actionTypes from "./search-types";
const INITIAL_STATE = {
query: "test",
results: ['test'],
};
const searchReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case actionTypes.UPDATE_QUERY:
return {
...state,
query: action.payload,
};
case actionTypes.UPDATE_RESULTS:
return {
...state,
results: action.payload,
};
default:
return state;
}
};
export default searchReducer;
// rootReducer.js
const rootReducer = combineReducers({
shop: shopReducer,
search: searchReducer,
});
export default rootReducer;
You need to destructure it
const Searchbar = ({ query, results, UpdateQuery, UpdateResults }) => {
Similar to mapStateToProps the mapDispatchToProps will be available to the connected Component as a Props
You would need to get the UpdateQuery and UpdateResults from the props and not directly use them for this to work like so :-
const Searchbar = ({ query, results,UpdateQuery, UpdateResults }) => {
const onSubmit = (e) => {
e.preventDefault();
};
const onChange = (e) => {
UpdateQuery(e.target.value);
};

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.

How can I repeatedly filter an array?

So, now i'm making to-do-list, and i have problems with buttons 'active' and 'done' tasks. When i press one of these button, it has to return tasks which are done/active, and it returns, but only 1 time. I guess it makes a new array, and delete old array. So how to make filter, which won't delete my array and just filter tasks which are done or active? And every time I click on these buttons, I will be shown tasks filtered on done/active/all.
P.S. sorry for ENG
onst ADD_TASK = 'ADD_TASK'
const EDIT_STATUS = 'EDIT_STATUS'
const TASK_DELETE = 'TASK_DELETE'
const DONE_TASK = 'DONE_TASK'
const ACTIVE_TASKS = 'ACTIVE_TASKS'
const initialState = {
tasks: []
};
const mainReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TASK: {
return {
...state,
tasks: [{
id: shortid.generate(),
task: action.task,
status: false
}, ...state.tasks], filter: 'all'
}
}
case EDIT_STATUS: {
return {
...state,
tasks: state.tasks.map(task => task.id === action.id ? {...task, status: !task.status} : task)
}
}
case TASK_DELETE: {
return {
...state,
tasks: state.tasks.filter(t => t.id !== action.id)
}
}
case DONE_TASK: {
return {
...state,
tasks: state.tasks.filter(t => !t.status),
filter: 'done'
}
return state.tasks
}
case ACTIVE_TASKS: {
return {
...state,
tasks: state.tasks.filter(t => t.status),
filter: 'active'
}
return state.tasks
}
default:
return state
}
}
export const doneTask = () => ({type: 'DONE_TASK'})
export const activeTask = () => ({type: 'ACTIVE_TASKS'})
export const addTask = task => ({type: 'ADD_TASK', task});
export const editStatus = id => ({type: 'EDIT_STATUS', id})
export const deleteTask = id => ({type: 'TASK_DELETE', id})
export default mainReducer;
Here is an example of how to store local state and pass it to ConnectedList as props.done.
ConnectedList has selectFilteredTasks as mapStateToProps and that is a selector created with reselect to get tasks, the second argument to this function is props so if props.done is not undefined it'll filter out the tasks that are done.
const { useState } = React;
const {
Provider,
connect,
} = ReactRedux;
const { createStore } = Redux;
const { createSelector } = Reselect;
const state = {
tasks: [
{
id: 1,
task: 'one',
status: false,
},
{
id: 2,
task: 'two',
status: true,
},
],
};
const store = createStore(
(x) => x, //won't dispatch any actions
{ ...state },
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
);
//selectors
const selectTasks = (state) => state.tasks;
const selectFilteredTasks = createSelector(
selectTasks,
(_, { done }) => done, //get the second argument passed to selectFilteredTasks
(tasks, done) =>
done !== undefined
? {
tasks: tasks.filter(
(task) => task.status === done
),
}
: { tasks }
);
const List = ({ tasks }) => (
<ul>
{tasks.map((task) => (
<li key={task.id}>
<pre>{JSON.stringify(task)}</pre>
</li>
))}
</ul>
);
const ConnectedList = connect(selectFilteredTasks)(List);
const App = () => {
const [done, setDone] = useState();
return (
<div>
<label>
only done
<input
type="checkbox"
onClick={() => setDone(done ? undefined : true)}
></input>
</label>
<ConnectedList done={done} />
</div>
);
};
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>
I suggest you to go with different approach.
In button click function, you can get all todos and return filtered out todos which are active/completed instead of performing operation on reducer.

Categories

Resources