I am doing a project with React y React-Redux
I am using an api, create a Search component to bring data from this api but I do not know how to pass the word (from what is searched) of redux to the component.
If I want to look for the word "pasta", I don't know how I should pass it on. I'm learning how to use Redux
----- REDUX ----
const INITIAL_STATE = {
search: '',
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case SEARCH: return {
...state,
recipes: action.payload,
};
default:
return {...state}
}
};
export function getByName(query) {
return function (dispatch) {
return axios.get("https://www.themealdb.com/api/json/v1/1/search.php?s="+query).then((response) => {
dispatch({
type: SEARCH,
payload: response.data.meals
})
}).catch((error) => console.log(error));
}
}
---- COMPONENTE SEARCH ---
const [search, setSearch ] = useState('')
const query = useSelector((state) => state.recipeReducer.search);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getByName(query))
}, [dispatch])
const handleFilter = e => {
e.preventDefault();
setSearch(e.target.value)
dispatch(getByName(search))
}
return (
<div>
<form>
<label>
Search:
<input type="text" id="title" placeholder='Search recipe...' value={search} onChange={(e) => handleFilter(e)} />
</label>
</form>
</div>
)
}
One thing I noticed is that the "search" in your initial state is redundant. The results are the thing you care about for this toy problem. You should have:
const INITIAL_STATE = {
recipes: [],
}
Then the issue is the construction of your search component. This is the component which is defining your query, not reading it.. Something like this would be more like what you want:
const SearchComponent = ({}) => {
const [search, setSearch] = useState('')
const recipes = useSelector((state) => state.recipeReducer.recipes);
const dispatch = useDispatch();
const handleFilter = e => {
e.preventDefault();
setSearch(e.target.value)
getByName(search)(dispatch) // getByName returns a function.
// That function takes dispatch as an argument.
}
return (
<div>
<form>
<label>
Search:
<input type="text" id="title" placeholder='Search recipe...' value={search} onChange={(e) => handleFilter(e)} />
</label>
</form>
</div>
);
}
Related
After showing the content for searched item, while removing the letters from search bar not showing the contents correctly. How to show the contents based on the word which is there in search bar. I have started to learn redux. So need some suggestions
import logo from "./logo.svg";
import "./App.css";
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
function App() {
const [name, setName] = useState("");
const [searchTerm, setSearchterm] = useState("");
const dispatch = useDispatch();
const data = useSelector((state) => state.add);
console.log(data, "dfata");
const handleChange = (e) => {
setName(e.target.value);
};
console.log(name);
if (data.length == 0) {
return <p>No data</p>;
}
const Submithandler = () => {
dispatch({ type: "ADD_ITEM", name });
setName("");
};
const handleSearch = (e) => {
setSearchterm(e.target.value);
};
const submitSerach = () => {
dispatch({ type: "SEARCH_ITEM", searchTerm });
};
const reset = () => {
dispatch({ type: "RESET", searchTerm });
};
return (
<div className="App">
{data.loading && <p>loading</p>}
<input value={searchTerm} onChange={(e) => handleSearch(e)} />
<button onClick={() => submitSerach()}>search</button>
<button onClick={() => reset()}>reset</button>
<input value={name} onChange={handleChange} />
<button onClick={Submithandler}>Add</button>
{data.item.length === 0 && <p>no item</p>}
{data.item.map((dta, i) => {
return (
<div>
{dta}
<button
onClick={() => dispatch({ type: "REMOVE_ITEM", name: dta })}
>
Remove
</button>
</div>
);
})}
</div>
);
}
export default App;
const INITIAL_STATE = {
item: [],
loading: false,
};
function addReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case "ADD_ITEM":
console.log(action, "ahghsgda");
return { item: [...state.item, action.name] };
case "REMOVE_ITEM":
console.log(action, "REMOPVE");
return {
item: state.item.filter((inditem) => inditem !== action.name),
};
case "SEARCH_ITEM":
console.log(action, "ahghsgda");
const data = [...state.item];
return {
loading: true,
item: [data.filter((product) => product.includes(action.searchTerm))],
};
case "RESET":
return {
item: [...state.item],
};
default:
return state;
}
}
export default addReducer;
After showing the content for searched item, while removing the letters from search bar not showing the contents correctly
I'm currently working on a project to implement a website to check the weather forecast.
I'm trying to get the value from the input field and when I click the submit button, this value should be set to cityName. What do I have to change in order to make this work?
import { useState, useEffect } from "react"
export function WeatherInfo() {
const token: string = '7ebe7c2a03cd48c090a193437'
async function getCurrentWeather(cityName: string): Promise<any> {
const response = await fetch(`http://api.weatherapi.com/v1/current.json?key=${token}&q=${cityName}`)
const data = await response.json()
console.log(data)
return data
}
const [cityName, setCityName]: any = useState('')
const [cityWeather, setCityWeather] = useState({})
const [value, setValue] = useState('')
const handleChange = (event: any) => {
setValue(event.target.value)
}
const handleSubmit = (event: any) => {
event.preventDefault()
setCityName(value)
}
useEffect(() => {
async function fetchData() {
const cityWeather = await getCurrentWeather(cityName)
}
fetchData()
})
return (
<div >
<form onSubmit={handleSubmit}>
<input onChange={handleChange} placeholder="Type here" />
<button>Search</button>
</form>
</div>
);
}
You should add a dependency array to your effect hook so that it triggers whenever cityName changes.
Updating the cityWeather state should only be done via the setCityWeather function.
useEffect(() => {
if (cityName) { // only fetch when you've got a value
getCurrentWeather(cityName).then(setCityWeather);
}
}, [cityName]);
You should also try to use as few any types as possible, preferably none
// define stand-alone functions outside your components
// eg weather-api.ts
const token = "your-api-key";
export interface CurrentWeather {
temp_c: number;
feelslike_c: number;
// etc
}
export async function getCurrentWeather(
cityName: string
): Promise<CurrentWeather> {
// safely encode URL query params
const params = new URLSearchParams({
key: token,
q: cityName,
});
const response = await fetch(
`http://api.weatherapi.com/v1/current.json?${params}`
);
// don't forget to check for errors
if (!response.ok) {
throw response;
}
return response.json(); // will be cast to the `CurrentWeather` type
}
import { useState, useEffect, FormEventHandler } from "react";
import { getCurrentWeather, CurrentWeather } from "./weather-api";
export function WeatherInfo() {
const [cityName, setCityName] = useState("");
const [cityWeather, setCityWeather] = useState<CurrentWeather>(); // default undefined
const [value, setValue] = useState("");
useEffect(() => {
getCurrentWeather(cityName).then(setCityWeather).catch(console.error);
}, [cityName]);
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
setCityName(value);
};
return (
<div>
{cityWeather && (
<p>
The current temperature in {cityName} is {cityWeather.temp_c} °C
</p>
)}
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
placeholder="Type here"
/>
<button>Search</button>
</form>
</div>
);
}
My React component loads infinitely and I want it to load only depending on the data that I get from the database, the console.log("1") is only for testing how many times the component loads.
This is the component:
import React from "react";
import Axios from "axios";
import { useState, useEffect } from "react";
function Added() {
const [data, setData] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/").then((result) => {
setData(result.data);
});
}, [data]);
console.log("1");
return data.map((item) => {
return (
<div key={item._id}>
<h1>{item.finame}</h1>
<h1>{item.laname}</h1>
<h5>{item.age}</h5>
</div>
);
});
}
export default Added;
This is where it loads:
import "./App.css";
import { useState, useReducer, useEffect } from "react";
import Added from "./added";
import Axios from "axios";
function App() {
const GettingALlTheData = () => {
return Axios.get("http://localhost:3001/").then((result) => {
return result.data;
});
};
/* -------------------- For the useReducer -------------------- */
const Actions = {
Add: "add",
};
const defaultState = {
list: [GettingALlTheData],
};
console.log(defaultState);
const reducer = (state, action) => {
switch (action.type) {
case Actions.Add:
const listItem = action.payload;
try {
Axios.post("http://localhost:3001/add", listItem);
} catch (error) {
console.log(error + "444444");
}
return { ...state, list: [...state.list, listItem] };
default:
console.log("this is the default");
}
};
const [state, dispatch] = useReducer(reducer, defaultState);
/* ---------------------------- For the form ---------------------------- */
const [listItem, setListItem] = useState({ finame: "", laname: "", age: 0 });
const [list, setList] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/").then((result) => {
state.list = result.data;
});
}, [state.list]);
const handelChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setListItem({ ...listItem, [name]: value });
};
const handelSubmit = (e) => {
e.preventDefault();
dispatch({ type: Actions.Add, payload: listItem });
};
const [data, setData] = useState({});
/* -------- for the useEffect to get the Data from the server -------- */
/* ------------------------ for the form return ---------------------- */
return (
<div className="App">
<h1>CRUD app using MERN stack</h1>
<form onSubmit={handelSubmit}>
<label htmlFor="finame">First name:</label>
<input
type="text"
name="finame"
id="finame"
value={listItem.finame}
onChange={handelChange}
/>
<label htmlFor="laname">Last name:</label>
<input
type="text"
name="laname"
id="laname"
value={listItem.laname}
onChange={handelChange}
/>
<label htmlFor="age">Age:</label>
<input
type="Number"
name="age"
id="age"
value={listItem.age}
onChange={handelChange}
/>
<button type="Submit">Submit</button>
</form>
{state.list ? (
<Added />
) : (
state.list.map((listItem) => {
return (
<div key={listItem._id}>
<h1>First name : {listItem.finame}</h1>
<h1>Last name: {listItem.laname}</h1>
<h3>Age: {listItem.age}</h3>
</div>
);
})
)}
</div>
);
}
export default App;
That's because you use the useEffect function with no dependency, so it is executed every time any prop/state changes (it's like a class component's componentDidUpdate).
I suggest you use it inside your Added component like a componentDidMount, so that it only execute once. To do it, you have to pass an empty dependency array, like so:
useEffect(() => {
//fetching the data
}, []);
I am building an application that fetches a player's details, using the input. But the api only allows fetching the details using player's id, hence I have to use another method to first get the id using player's name. But there is some problem getting the input. I also tried using e.target.value, but it isn't working
import React, { useEffect, useState } from 'react'
import HLTV from 'hltv';
// Getting player id using this fn.
const getPlayerIdByName = async (text) => {
return await HLTV.getPlayerByName({ name: text })
.then(res => res.id)
// .then(data => console.log(data))
.catch(err => console.log(err));
}
//Getting player stats using id obtained from above
const getPlayerStats = (playerId) => {
HLTV.getPlayerStats({ id: playerId })
.then(res => Object.entries(res))
}
const Search = () => {
const [name, setName] = useState('');
const [id, setId] = useState('');
useEffect(() => {
getPlayerIdByName(name)
.then(id => setId(id))
}, [name]);
const onChange = (e) => {
setName(e.target.value)
}
const onSubmit = (e) => {
e.preventDefault();
setName(name);
console.log(name)
}
return (
<div>
<form onSubmit={onSubmit} className="player">
<input type="text" value={name} placeholder="Enter Player's in game name" onChange={onChange} />
<button type="Submit" defaultValue="Search">Search</button>
</form>
</div>
)
}
export default Search;
I would refactor your code like this:
The main problem I see, is that you are using useEffect() to get the playerIdByName every time that name changes. Instead, just call that function inside the onSubmit handler. And instead of storing the id in state, store your stats instead.
Then, when you have stats in state, you can render them by maping the key value pairs.
import HLTV from 'hltv';
// Getting player id using this fn.
const getPlayerByName = async (text) => await HLTV.getPlayerByName({ name: text });
//Getting player stats using id obtained from above
const getPlayerStats = async (playerId) => await HLTV.getPlayerStats({ id: playerId });
const Search = () => {
const [name, setName] = useState('');
const [stats, setStats] = useState([]);
const onChange = (e) => {
setName(e.target.value);
};
const fetchStats = async () => {
const player = await getPlayerByName(name);
const stats = await getPlayerStats(player.id);
const statsEntries = Object.entries(stats);
setStats(statsEntries);
};
const onSubmit = async (e) => {
e.preventDefault();
try {
await fetchStats();
} catch (error) {
console.error(error);
}
};
return (
<div>
<form onSubmit={onSubmit} className="player">
<input
type="text"
value={name}
placeholder="Enter Player's in game name"
onChange={onChange}
/>
<button type="Submit" defaultValue="Search">
Search
</button>
</form>
{stats.length > 0 && (
<div>
{stats.map(([key, value]) => (
<p>
{key}: {value}
</p>
))}
</div>
)}
</div>
);
};
export default Search;
I am just taking my first steps with react and redux.
I started the project first without redux and now I have decided to implement it with redux.
The login worked before I adapted it to redux.
ThunkMiddleware is applied
Now the problem:
When I click the login button, the logger or DevTools only shows LOGIN_FAILURE. The page reloads and displays the login again.
If I change this
onSubmit={() => props.login(username, password)}
to this
onSubmit={props.login(username, password)}
LOGIN_REQEST actions are spammed and finally (if the password is stored in the browser) LOGIN_SUCCESS. I get the actual content with correct data from the server.
What do I have to change to make the login work normally?
Thanks for your help
LoginComponent:
function Login(props) {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
return (
<div>
<form onSubmit={() => props.login(username, password)}>
<TextField
onChange={e => setUsername(e.target.value)}
/>
<br/>
<TextField
onChange={e => setPassword(e.target.value)}
/>
<br/>
<Button type="submit">
Login
</Button>
</form>
</div>);
}
const mapDispatchToProps = dispatch => {
return {
login: (username, password) => dispatch(login(username, password))
}
};
export default connect(
null,
mapDispatchToProps
)(Login)
LoginAction
import {
LOGIN_FAILURE,
LOGIN_REQUEST,
LOGIN_SUCCESS
} from "./LoginTypes";
export const login = (username = '', password = '') => {
return (dispatch) => {
dispatch(loginRequest());
axios.post(`server`, {
//data
}).then(
(res) => {
dispatch(loginSuccess(res));
},
(err) => {
dispatch(loginFailure(err.message));
}
);
}
};
export const loginRequest = () =>{
return {
type: LOGIN_REQUEST
}
};
export const loginSuccess = tabs =>{
return {
type: LOGIN_SUCCESS,
payload: tabs
}
};
export const loginFailure = error =>{
return {
type: LOGIN_FAILURE,
payload: error
}
};
LoginReducer:
const LoginReducer = (state = initialState, action) => {
switch (action.type){
case LOGIN_REQUEST:
return {
...state,
loading: true
};
case LOGIN_SUCCESS:
let tabBars = populateArray1(action.payload);
let navIcons = populateArray2();
return{
...state,
loading: false,
tabBars: tabBars,
navIcons: navIcons,
isLoggedIn: true
};
case LOGIN_FAILURE:
return{
...state,
loading: false,
error: action.payload
};
default: return state;
}
};
component, which controls login and content:
function Main(props) {
if(props.auth){
return(
<NotLogin />
)
}
else{
return <Login />
}
}
Your login page is getting refresh/redirecting due to which its not handling the api request & its response properly. Please try this by updating your login component.
function Login(props) {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleLogin = (event) => {
event.preventDefault()
props.login(username, password);
}
return (
<div>
<form onSubmit={handleLogin}>
<TextField
onChange={e => setUsername(e.target.value)}
/>
<br/>
<TextField
onChange={e => setPassword(e.target.value)}
/>
<br/>
<Button type="submit">
Login
</Button>
</form>
</div>);
}
After updating that, please make sure that you are getting correct value in props.auth through redux in your Main component. The Main component should have redux connection with redux auth state in your code.