Why do I get Invalid Hook Call - reactjs

I'm trying to build a react web application using functional components, react hooks, and redux. I can't figure out why I am getting the invalid hook call. When I click the Search button I get the react error.
Here's the entirety of the code:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'
export const SearchBar = () => {
const searchParameters = useSelector(state => state.searchParameters ?? {});
const dispatch = useDispatch;
return (
<div className="searchbar">
<div className="search-parameters">
<BasicSearch searchParameters={searchParameters} />
<div className="col form-group">
<button type="button" className="btn btn-primary" onClick={() => dispatch(doSearch())} >
SEARCH
</button>
</div>
</div>
</div>
);
}
const BasicSearch = (props) => {
return (
<>
<input type="text" id="origin" />
</>
);
}
const Actions = {
DO_SEARCH: "DO_SEARCH"
}
const doSearch = () => {
return { type: Actions.DO_SEARCH };
}

useDispatch on your 6th line of code is a function, so you need to assign the variable dispatch to it like so:
const dispatch = useDispatch();

Update
const dispatch = useDispatch();
and
onClick={() => dispatch(doSearch)}

Related

How to dispatch two redux action on single onClick event

Here I am dispatching an action on button click but now I want to dispatch one more action from redux on the same button and the action which I want to dispatch is already imported on top named totalHandler so how am I supposed to do this thanks :)
import React from "react";
import { useParams } from "react-router-dom";
import "./ProductDetail.css";
import { useDispatch, useSelector } from "react-redux";
import { cartHandler } from "../../store/DataStore";
import { totalHandler } from "../../store/DataStore";
const Detail = () => {
const { id } = useParams();
const dispatch = useDispatch();
let data = useSelector((state) => state.data.DUMMY_DATA);
data = data.filter((val) => val.product_id === id);
data = data[0];
return (
<div className="detail_wrapper">
<div>
<img src={data.product_image} alt="" className="detail_image" />
</div>
<div className="inner">
<div className="detail_title">{data.product_title}</div>
<div className="detail_description">{data.product_description}</div>
<div className="detail_price">{data.product_price}</div>
<button
className="button"
onClick={() => dispatch(cartHandler(data.product_id))}
>
Add to Cart
</button>
</div>
</div>
);
};
export default Detail;
<button className="button" onClick={()=>{dispatch(cartHandler(data.product_id));dispatch(totalHandler())}}>Add to Cart</button>
OR create a function like
function Dispatch(){
dispatch(totalHandler());
dispatch(cartHandler(data.product_id));
}
<button className="button"
onClick={Dispatch}>Add to Cart</button>
Just add braquet at the right place {}
import React from "react";
import { useParams } from "react-router-dom";
import "./ProductDetail.css";
import { useDispatch, useSelector } from "react-redux";
import { cartHandler } from "../../store/DataStore";
import { totalHandler } from "../../store/DataStore";
const Detail = () => {
const { id } = useParams();
const dispatch = useDispatch();
let data = useSelector((state) => state.data.DUMMY_DATA);
data = data.filter((val) => val.product_id === id);
data = data[0];
return (
<div className="detail_wrapper">
<div>
<img src={data.product_image} alt="" className="detail_image" />
</div>
<div className="inner">
<div className="detail_title">{data.product_title}</div>
<div className="detail_description">{data.product_description}</div>
<div className="detail_price">{data.product_price}</div>
<button
className="button"
onClick={() => {dispatch(cartHandler(data.product_id));
dispatch(cartHandler(data.product_id_2))}}
>
Add to Cart
</button>
</div>
</div>
);
};
export default Detail;
One little precision because I have been struggling because of that detail, don't forget to await if necessary, in my case:
function handleClick(dice: DiceViewModel) {
return dice.isTenzies
? async () => {
await dispatch(initializeDice())
await dispatch(rollDice())
}
: () => dispatch(rollDice())
}

what is the design flaw causing the infinite loop in this component?

I've created a basic React component as a sandbox. I added a doSomething() function to the component which simply displays an alert on useEffect(). However, the alert is getting called in an infinite loop. So obviously, there's something wrong with the design. Any idea what the design issue is with this component which is causing the infinite loop? What steps would you recommend to redesign this component in order to fix that issue?
import {Repository} from '../data/Repository';
import { useEffect , useState} from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { decrement, increment } from '../features/counter/counterSlice';
import { setIsValid } from '../features/userManagement/userManagementSlice';
export const UserManagement = () => {
const [userTestData, setUserTestData] = useState([]);
const [userCount, setUserCount] = useState(0);
const count = useSelector((state) => state.counter.value)
const isValid2 = useSelector((state) => state.userManagement.isValid2)
const dispatch = useDispatch()
const doSomething = () => {
alert('about to do something');
}
useEffect(() => {
doSomething();
setUserTestData(Repository.getUserTestData());
})
useEffect(() => {
setUserCount(userTestData.length);
}, [userTestData])
//debugger;
return(
<>
<div>
{
userTestData.map((x) => {
return <div key={x.Id}>{x.FirstName}</div>
})
}
</div>
<div>there are {userCount} users</div>
<div>
{/* PARENT INPUT VALIDATION */}
<div style={{height:100}}>
<button onClick={() => dispatch(setIsValid())}>
TOGGLE VALIDATION
</button>
{isValid2 ? 'true' : 'false'}
</div>
{/* COUNTER */}
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
</>
)
}
The issue here.
useEffect(() => {
doSomething();
setUserTestData(Repository.getUserTestData());
})
You didn't add array of dependences to useEffect, so it's callback will be excuted on each render of the component, and as you set a State by setUserTestData, so the component will still re-render in infinite loop.
To fix.
useEffect(() => {
doSomething();
setUserTestData(Repository.getUserTestData());
}, [])

Back functionality of browser is not working

I have written the following code with a Query string corresponding to the filters applied. On clicking browser's back button I want to get back to the previous set of filters, but instead I land back to the browser's home page every time.
import React, {useState, useEffect, useCallback} from 'react'
import {DropDownList, MultiSelect} from "#progress/kendo-react-dropdowns"
import {Button} from '#progress/kendo-react-buttons'
import { useHistory, useLocation } from "react-router-dom"
const Filter = () => {
const history=useHistory()
const location=useLocation()
const [Standard,setStandard] = useState("All")
const [Hobbies,setHobbies] = useState(["Playing"])
const [Responsibility,setResponsibility] = useState("All")
const [QueryString,setQueryString] = useState(location.search.substring(1))
const options={
StandardList:["All","VI","VII","VIII"],
HobbiesList: ["Playing", "Drawing","Swimming"],
ResponsibilityList:["All","Monitor","Head","Sports Captain"]
}
const handleApply = ()=>{
setQueryString(`Standard=${JSON.stringify(Standard)}&Responsibility=${JSON.stringify(Responsibility)}&IncidentStatus=${JSON.stringify(Hobbies)}`)
}
const backButtonEvent = useCallback((e) => {
e.preventDefault();
history.goBack()
},[])
useEffect(() => {
window.addEventListener('popstate',backButtonEvent);
return ()=>{
window.removeEventListener('popstate',backButtonEvent);
}
},[backButtonEvent])
useEffect(() => {
var params= new URLSearchParams((QueryString)?(QueryString):`Standard=${JSON.stringify(Standard)}&Responsibility=${JSON.stringify(Responsibility)}&IncidentStatus=${JSON.stringify(Hobbies)}`)
history.push({search: params.toString()})
}, [QueryString])
return (
<div>
<label>Standard </label>
<DropDownList data={options.StandardList} defaultValue={"All"} value={Standard}
onChange={(event)=>setStandard(event.target.value)}/>
<label > Hobbies </label>
<MultiSelect data={options.HobbiesList} defaultValue={["Playing"]} value={Hobbies} onChange={(event)=>setHobbies([...event.value])}/>
<label > Responsibility </label>
<DropDownList data= {options.ResponsibilityList} defaultValue= {"All"} value={Responsibility} onChange={(event)=>setResponsibility(event.target.value)} />
<Button id="submitFilter" type="button" onClick={handleApply} > Apply </Button>
</div>
)
}
export default Filter
Why am I getting this behaviour and how can I resolve it?

how to get input value of a json data when submitted

I have json data file. I want to make a search with the names of the data and display the typed names when I click the search button.
I get the value of input in the console when I type something however I am not able to display it on the screen
How can I display the value of this input ?
my code is below
PostResults.jsx
import React from "react";
const PostResults = (props) => {
const {name} = props.posts
return(
<div className="">
<p className="titles">{name}</p>
</div>
)
}
export default PostResults
Posts.jsx
import React, { useState, useEffect } from "react";
import PostResults from './PostResults'
const Posts = (props) => {
const [posts, setPosts] = useState([]);
const [searchTerm,setSearchTerm]=useState([]);
const getData = () => {
fetch('data.json')
.then(response => {
return response.json()
//console.log(response)
})
.then(data => {
setPosts(data)
console.log(data)
})
}
useEffect(() => {
getData()
},[])
const submitHandler = (event) => {
event.preventDefault()
{searchTerm ? searchTerm : console.log("none")}
}
return(
<div className="">
<input
type="text"
placeholder="Search Anything"
name="query"
onChange={e => setSearchTerm(e.target.value)}
className="search-input"
/>
<button
onClick={submitHandler}
type="submit"
className="search-button"
>
<i className="fas fa-search"></i>
</button>
{posts.map(posts => (
<PostResults key={posts.id} posts={posts}/>
))}
</div>
)
}
export default Posts
App.jsx
import React from "react";
import "./style.css";
import 'bootstrap/dist/css/bootstrap.min.css'
import Posts from './components/Posts'
export default function App() {
return (
<div className="container">
<div className="row">
< Posts />
</div>
</div>
);
}

Delete an item on click

I am trying to write the very first to-do application in REACT. I want to add functionality to delete to-do item when the user clicks on the delete icon. When I click on delete icon it only removes the text. Here I would like to delete the entire item. Can someone please suggest?
App.js
import './App.css';
import { useState } from 'react';
import TodoList from './TodoList';
import { v4 as uuidv4 } from 'uuid';
function App() {
const [input, setInput] = useState('');
const [todos, setTodo] = useState([]);
const addTodo = (e) => {
e.preventDefault();
const id = uuidv4();
setTodo([...todos, { id: id, text: input}])
// setTodo({todos: [...todos, input], id })
setInput('');
}
const deleteTodo = (id) => {
console.log("id" + id);
const filteredItem = todos.filter(todo => todo.id !== id);
setTodo([filteredItem]);
}
return (
<div className="App">
<form>
<input type="text" value={input} onChange={(e) => setInput(e.target.value)}/>
<button type="submit" onClick={addTodo}>Enter</button>
</form>
<TodoList todos={todos} deletetodo={deleteTodo}/>
</div>
);
}
export default App;
TodoList.js
import React from 'react'
import DeleteIcon from '#material-ui/icons/Delete';
import EditIcon from '#material-ui/icons/Edit';
const todo = ({todos, deletetodo}) => {
return (
<div>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<div>
<DeleteIcon onClick={(todo) => deletetodo(todo.id)}/>
<EditIcon/>
</div>
</li>
))}
</div>
)
}
export default todo;
There are a few problems with your code. I will start with the most obvious. You re-render your App on EVERY change of the input field. That's just unnecessary. So insated of storing the value of the input in a state variable, I would use useRef(). So you only really need one state variable, one that stores the list of todos.
Second, your filter is correct, but then you incorrectly set the state variable with the filtered result:
const filteredItem = todos.filter(todo => todo.id !== id);
setTodo([filteredItem]);
It will already return an array and there is no need to wrap it into another one.
With those 2 main issue fixed, here is a working example along with a Sandbox:
import React, { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./styles.css";
const TodoList = ({ todos, deletetodo }) => {
return (
<div>
{todos.map((todo) => (
<li key={todo.id}>
{todo.text}
<div>
<button onClick={() => deletetodo(todo.id)}>delete</button>
<button>edit</button>
</div>
</li>
))}
</div>
);
};
export default function App() {
const [todos, setTodo] = useState([]);
const input = React.useRef();
const addTodo = (e) => {
e.preventDefault();
const id = uuidv4();
setTodo([...todos, { id: id, text: input.current.value }]);
input.current.value = "";
};
const deleteTodo = (id) => {
setTodo(todos.filter((item) => item.id !== id));
};
return (
<div className="App">
<form>
<input ref={input} type="text" />
<button type="submit" onClick={addTodo}>
Enter
</button>
</form>
<TodoList todos={todos} deletetodo={deleteTodo} />
</div>
);
}
You have a mistake in how you're setting todo in deleteTodo:
const deleteTodo = (id) => {
console.log("id" + id);
const filteredItem = todos.filter(todo => todo.id !== id);
// Mistake! Your filteredItem is an array, you're putting your array into an array.
setTodo([filteredItem]);
}
Consequently, when you pass it further down, your component tries to get [filteredItem].text, which is undefined and React sees an empty string.
Fix:
setTodo(filteredItem);
There are multiple issues within the code:
First one is setting the values after deleting the row:
should be like this : setTodo(filteredItem);
Second issue was calling the onClick function, you already have the id with you so no need to re-call it again:
<div>
{todos.map(todoss =>
<li onClick={() => deletetodo(todoss.id)} key={todoss.id}>
{todoss.text}
</li>
)}
</div>

Resources