Share State between two components [ Hooks ] - javascript

I need help, is there any possible way to send the useEffect submittedInput from search.js to AllThemeContext.js to use it as value of Provider ?
Both are in two different folders [adjacent components]
/Search Js/
/*Import*/
import React, { useState } from "react";
import "./Search.scss";
/*Component*/
const Search = () => {
/************************************************************************************************/
/***************************************************The Logic Area/
/****************************************************************/
/*The state used*/
const [input, setInput] = useState("");
const [submittedInput, setSubmittedInput] = useState("");
/*Functions used*/
// onFormSubmit
const onFormSubmit = (e) => {
e.preventDefault();
setInput("");
};
/************************************************************************************************/
/*************************************************** The Return Area/
/****************************************************************/
return (
<>
<div className="Search">
<form onSubmit={onFormSubmit} className="Search__form">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
type="text"
placeholder=" Title, companies, expertise, or benefits"
style={{ fontFamily: "Poppins, FontAwesome" }}
></input>
<button onClick={() => setSubmittedInput(input)}>Search</button>
</form>
</div>
</>
);
};
/************************************************************************************************/
/***************************************************The Component End Area/
/****************************************************************/
export default Search;
/All Theme context/
import React, { createContext, useState } from "react";
export const AllContext = createContext();
/*Provider*/
const AllContextProvider = (props) => {
const [input, setInput] = useState();
const [numbs, setNumbs] = useState(1);
return (
<AllContext.Provider value={{ input, numbs }}>
{props.children}
</AllContext.Provider>
);
};
export default AllContextProvider;

Related

validate a form in the parent component react

I have a two inputs in my child component, I would like to pass the event of the inputs to my parent component. In the parent component I have a function which will handle the submission. I have the two useState in the parent component, just in case I need to use it in the future in other condition to ensure the user is logged in. I am wondering how to achieve this ? or am I taking the wrong approach with having the usestates in my parent component ?
import { useState } from "react";
import Child from './Child'
import "./styles.css";
export default function Parent() {
const [login, setLogin] = useState(null);
const [password, setPassword] = useState(null);
const loginhandler = ()=>{
if (!login && !password){
console.log("alert error")
} else {
console.log('you are logged in')
}
}
return (
<>
<Child/>
</>
);
}
import { useState } from "react";
import "./styles.css";
export default function Parent() {
const [login, setLogin] = useState(null);
const [password, setPassword] = useState(null);
return (
<>
<input
placeholder="Id"
value={login}
onChange={(e) => setLogin(e.target.value)}
/>
<input
placeholder="Password"
value={password}
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
</>
);
}
you can pass function like this:
const setParentState = (val) => setLogin;
how do you pass this function to the child component:
<Child setParentPassword={setParentState} />
this way you can use the child components variables and use or set it in parent state
Flow should be like this :
import { useState } from "react";
import Child from './Child'
import "./styles.css";
export default function Parent() {
const [login, setLogin] = useState(null);
const [password, setPassword] = useState(null);
const loginhandler = ()=>{
//your logic will goes here
if (!login && !password){
console.log("alert error")
} else {
console.log('you are logged in')
}
}
return (
<>
<Child
loginhandler={loginhandler}
setLogin={setLogin}
setPassword={setPassword}
/>
</>
);
}
import { useState } from "react";
import "./styles.css";
export default function Child({loginhandler, setLogin, setPassword}) {
return (
<>
<input
placeholder="Id"
value={login}
onChange={(e) => setLogin(e.target.value)}
/>
<input
placeholder="Password"
value={password}
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={loginhandler}>LOGIN</button>
</>
);
}

Can't update parent component

I'm trying to edit an input value in a child component and send to the parent
:
https://codesandbox.io/s/sleepy-rain-skoss?file=/src/Editlabel.js:0-389
Parent:
import "./styles.css";
import EditLabel from "./Editlabel";
import { useEffect, useState } from "react";
export default function App() {
const [newName, setNewName] = useState();
useEffect(() => {
console.log("newName", newName);
}, [newName]);
return (
<div className="App">
<EditLabel
value={"hello"}
click={(changedName) => {
setNewName(changedName);
}}
/>
</div>
);
}
Child:
import React, { useState } from "react";
const EditLabel = ({ value, click }) => {
const [name, setName] = useState(value);
return (
<>
<input type={"text"} placeholder={name}></input>
<button
onClick={(e) => {
setName(e.target.value);
click(name);
}}
>
Edit
</button>
</>
);
};
export default EditLabel;
However, the console logs "hello" and then it just logs empty strings.
How can I make it work?
try this on your child's input box
<input type={"text"} placeholder={name} onChange={(e) => setName(e.target.value)}>
Change EditLabel to use a ref to capture the input value:
const EditLabel = ({ value, click }) => {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} type={"text"} placeholder={value}></input>
<button
onClick={() => {
click(inputRef.current.value);
}}
>
Edit
</button>
</>
);
};
Update App to use the values it gets via the click callback:
export default function App() {
const [newName, setNewName] = useState("hello");
useEffect(() => {
console.log("newName", newName);
}, [newName]);
return (
<div className="App">
<EditLabel
value={newName}
click={(changedName) => {
setNewName(changedName);
}}
/>
</div>
);
}

TypeError: list.map not a function

I'm building an app for React Practice and I'm getting an error while trying to store an array of objects to local storage. I'm a beginner at React so I'm not sure what's going on.
I get the error in the IncomeOutputList component, because I am trying to list a bunch of objects using an array.
Here is my code:
App component:
import React, { useState, useReducer } from 'react';
import BudgetInput from './components/input/BudgetInput';
import IncomeOutputList from './components/output/IncomeOutputList';
import IncomeOutput from './components/output/IncomeOutput';
const useSemiPersistentState = (key, initialState) => {
const [value, setValue] = React.useState(
localStorage.getItem(key) || initialState
);
React.useEffect(()=>{
localStorage.setItem(key, JSON.stringify(value));
}, [value, key])
return [value, setValue];
};
const App = () => {
const [incomes, setIncomes] = useSemiPersistentState('income',[{}]);
const [description, setDescription] = useState('');
const [type, setType] = useState('+');
const [value, setValue] = useState('');
const incomeObj = {
desc: description,
budgetType: type,
incomeValue: value
}
const handleIncomeObjArray = () => {
setIncomes(incomes.concat(incomeObj));
console.log(incomes + "testing");
}
const handleChange = (event) => { //this handler is called in the child component BudgetInput
setDescription(event.target.value);
}
const handleSelectChange = (event) => { //this handler is called in the child component BudgetInput
setType(event.target.value);
}
const handleValueChange = (event) => {
setValue(event.target.value);
console.log(incomeObj)
}
return (
<div className="App">
<div className="top">
</div>
<div className="bottom">
<BudgetInput
descValue={description}
onDescChange={handleChange}
onSelectChange={handleSelectChange}
type={type}
onBudgetSubmit={handleIncomeObjArray}
budgetValue={value}
onValChange={handleValueChange}
/>
{/* <IncomeOutput
obj={incomeObj}
/> */}
{/* <IncomeOutput
desc={description}
type={type}
/> */}
<IncomeOutputList
list={incomes}
/>
</div>
</div>
)
};
IncomeOutputList component:
import React from 'react';
import IncomeOutput from './IncomeOutput';
// list will be list of income objects
const IncomeOutputList = ({ list }) => {
return (
<div>
{list.map(item => <IncomeOutput
id={item.id}
value={item.incomeValue}
type={item.budgetType}
desc={item.desc}
/>)}
</div>
)
}
export default IncomeOutputList;
IncomeOutput component:
import React from 'react';
import ValueOutput from './ValueOutput';
const IncomeOutput = ({ desc, type,id, value }) => {
//id = inc-{id}
return (
<>
<div className="item clearfix" id={id}>
<div className="item__description">{desc}</div>
<ValueOutput
type={type}
value={value}
/>
</div>
</>
)
}
export default IncomeOutput;
EDIT: here is a codesandbox for this code: https://codesandbox.io/s/busy-shirley-bwhv2?file=/src/App.js:2394-2509
It's because your Custom reducer return a string and not an array
try this
const useSemiPersistentState = (key, initialState) => {
const [value, setValue] = React.useState(
localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
);
React.useEffect(()=>{
localStorage.setItem(key, JSON.stringify(value));
}, [value, key])
return [value, setValue];
};

React.js: how to create search filter?

I am trying to make work search input. I'm filtering through fetched data in useEffect in Hooks/useCountries component, listening to input in App.js and passing props for handleChange in Searchbar component. Something is missing, I can't figure out what. Here is Hooks/useCountries component
import React, { useState, useEffect } from "react";
export default function useCountries(search) {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const fetchData = () => {
fetch("https://restcountries.eu/rest/v2/all")
.then((res) => res.json())
.then((result) => setData(result))
.catch((err) => console.log("error"));
};
useEffect(() => {
const searchResult =
data &&
data
.filter((item) => item.name.toLowerCase().includes(search))
.map((element) => <div>{element.name}</div>);
}, []);
useEffect(() => {
fetchData();
}, []);
return [data, error];
}
App.js
import React, { useState } from "react";
import SearchBar from "./components/SearchBar";
import useCountries from "./Hooks/useCountries";
import MainTable from "./components/MainTable";
import "./App.scss";
export default function App() {
const [search, setSearch] = useState("");
const [data, error] = useCountries(search);
const handleChange = (e) => {
setSearch(e.target.value);
};
return (
<div className="App">
<SearchBar handleChange={handleChange} search={search} />
<MainTable countries={data} />
</div>
);
}
SearchBar component
import React, { useState } from "react";
import "./SearchBar.scss";
export default function Searchbar({ handleChange, search }) {
return (
<div className="SearchBar">
<input
className="input"
type="text"
placeholder="search country ..."
value={search}
onChange={handleChange}
/>
</div>
);
}
The useEffect method you have which performs the filtering needs to be fired each time the search term changes - currently you are only using it once when the hook is created for the first time:
useEffect(() => {
const searchResult =
data &&
data
.filter((item) => item.name.toLowerCase().includes(search))
.map((element) => <div>{element.name}</div>);
}, [search]);
Note how the search variable is now part of the useEffect dependency array.

How to assign value onClick from different component to another component in React

What i want to do :
When i click my button i.e Search in Navbar.js i want to assign the search text in the variable urlQuery so i can pass it as props in Episodes.js component
End goal is to pass the urlQuery from Navbar.js somehow to Episodes.js component so i can query the REST api
How do i achieve the desired behaviour pls help
App.js
import React, { useState } from 'react';
import './App.css'
import Episodes from './components/Episodes/Episodes'
import CustomNavbar from './components/Navbar/Navbar'
import Pagination from './components/Pagination/Pagination'
function App() {
const [postsPerPage] = useState(20);
const [currentPage, setCurrentPage] = useState(1);
const url=`https://rickandmortyapi.com/api/episode?page=${currentPage}`
let urlQuery = `https://rickandmortyapi.com/api/episode?name=${SEARCH TEXT HERE}`
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<div>
<CustomNavbar />
<Episodes
urlQuery={urlQuery}
url={url}
/>
<Pagination
postsPerPage={postsPerPage}
totalPosts={36}
paginate={paginate}
/>
</div>
);
}
export default App;
Navbar.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import FormControl from 'react-bootstrap/FormControl';
const customNavbar = () => {
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">Rick And Morty</Navbar.Brand>
<Form inline>
<FormControl type="text" placeholder="Search" />
<Button>Search</Button>
</Form>
</Navbar>
);
}
export default customNavbar
Edit
On Zohaib's suggestion this error is thrown
Failed to compile.
./src/components/Navbar/Navbar.js
Line 14:48: Unexpected use of 'event' no-restricted-globals
Search for the keywords to learn more about each error.
App.js
import React, { useState, useEffect } from 'react';
import './App.css'
import Episodes from './components/Episodes/Episodes'
import CustomNavbar from './components/Navbar/Navbar'
import Pagination from './components/Pagination/Pagination'
function App() {
const [postsPerPage] = useState(20);
const [currentPage, setCurrentPage] = useState(1);
const [userSearchValue, setUserSearchValue] = useState('');
const [url, setUrl] = useState(``);
const [urlQuery, setUrlQuery] = useState(``)
useEffect(() => {
setUrl(`https://rickandmortyapi.com/api/episode?page=${currentPage}`)
}, [currentPage]);
useEffect(() => {
setUrlQuery(`https://rickandmortyapi.com/api/episode?name=${userSearchValue}`)
}, [userSearchValue])
const paginate = pageNumber => setCurrentPage(pageNumber);
const handleButtonClick = (searchValue) => {
setUserSearchValue(searchValue);
}
return (
<div>
<CustomNavbar
onButtonClick={handleButtonClick}
/>
<Episodes
urlQuery={urlQuery}
url={url}
/>
<Pagination
postsPerPage={postsPerPage}
totalPosts={36}
paginate={paginate}
/>
</div>
);
}
export default App;
Navbar.js
import React, { useState } from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import FormControl from 'react-bootstrap/FormControl';
const customNavbar = ({ onButtonClick }) => {
const [searchValue, setSearchValue] = useState('');
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">Rick And Morty</Navbar.Brand>
<Form inline>
<FormControl type="text" placeholder="Search" value={searchValue} onChange={(e) => setSearchValue(e.target.value)} />
<Button onClick={() => onButtonClick(searchValue)}>Search</Button>
</Form>
</Navbar>
);
}
export default customNavbar
The important part here is you're passing down the handleButtonClick function to the child component (Navbar). This way you can call that parent function in the child component whenever you want (ie. when the user clicks the submit button).
Do you mean something like this?
There is a React guide about this specific problem: Lifting State Up.
Normally what you do is you manage the state in the parent. In this case App where you manage the search text state. You pass down a function to components to change this state. The components that depend upon this state are passed the value through the properties.
Here is an example:
const {useEffect, useState} = React;
function App() {
const episodesURL = "https://rickandmortyapi.com/api/episode";
const [page, setPage] = useState(1);
const [pageInfo, setPageInfo] = useState({});
const [searchText, setSearchText] = useState("");
const [episodes, setEpisodes] = useState([]);
useEffect(() => {
const url = new URL(episodesURL);
url.searchParams.set("page", page);
if (searchText) url.searchParams.set("name", searchText);
fetch(url)
.then(response => response.json())
.then(response => {
if (response.error) {
setPageInfo({});
setEpisodes([]);
} else {
setPageInfo(response.info);
setEpisodes(response.results);
}
});
}, [page, searchText]);
const search = searchText => {
setSearchText(searchText);
setPage(1);
};
return (
<div>
<CustomNavbar search={search} />
<Episodes episodes={episodes} />
<Pagination setPage={setPage} info={pageInfo} />
</div>
);
}
function CustomNavbar({search}) {
const [searchText, setSearchText] = useState("");
const handleFormSubmit = event => {
event.preventDefault();
search(searchText);
};
return (
<form onSubmit={handleFormSubmit}>
<input
type="text"
placeholder="search"
value={searchText}
onChange={event => setSearchText(event.target.value)}
/>
<button type="submit">Search</button>
</form>
);
}
function Episodes({episodes}) {
return (
<table>
<thead>
<tr>
<th>episode</th>
<th>name</th>
<th>air date</th>
</tr>
</thead>
<tbody>
{episodes.map(episode => (
<tr key={episode.id}>
<td>{episode.episode}</td>
<td>{episode.name}</td>
<td>{episode.air_date}</td>
</tr>
))}
</tbody>
</table>
);
}
function Pagination({setPage, info}) {
return (
<div>
{info.prev && <a onClick={() => setPage(page => page - 1)}>previous</a>}
{info.next && <a onClick={() => setPage(page => page + 1)}>next</a>}
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
th { text-align: left; }
a { cursor: pointer; }
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Change urlQuery to state variable. Then, pass setUrlQuery to NavBar as a prop and on search button clickEvent call setUrlQuery function.

Categories