Pass a variable in a functional component to a class component - reactjs

I've a ToysPage.js with a class component and a SearchFeature.js with a functional component (child of ToysPage).
I made a searchBar in SearchFeature.js with hooks and with console.log I can see that it works. But how can I pass the const filteredToys to the state toysFiltered in ToysPage.js?
ToysPage.js
import React, { Component } from "react";
export default class ToysPage extends Component {
state = {
toys: undefined,
toysFiltered: undefined,
};
//Here I call all the toys through Axios and insert them in toys and toysFiltered
return (
<>
<div className="container">
<div className="row">
<SearchSide
toysFiltered={this.state.toysFiltered}/>
<ToysSide toys={this.state.toysFiltered} />
</div>
</div>
</>
);
}
}
function SearchSide({ toysFiltered }) {
return (
<>
<div className="col-3">
<SearchFeature toysFiltered={toysFiltered} />
</div>
</>
);
}
SearchFeature.js
import React, { useState, useMemo } from "react";
export default function SearchFeature({ toysFiltered }) {
const [query, setQuery] = useState("");
const [filteredToys, setFilteredToys] = useState(toysFiltered);
useMemo(() => {
const result = toysFiltered.filter((toy) => {
return toy.titulo.toLowerCase().includes(query.toLowerCase());
});
setFilteredToys(result);
}, [toysFiltered, query]);
return (
<div className="form-group">
<label>Search a Toy</label>
<input
type="text"
className="form-control"
placeholder="Search"
value={query}
onChange={(e) => {
setQuery(e.target.value);
}}
/>
</div>
);
}

You have some state in a child component, SearchFeature, that you would like in the parent component ToysPage. The usual pattern for this is to lift the state out of SearchFeature, which you have partially done.
You're almost doing this correctly. You pass a list of filtered toys into SearchFeature, but that list is only being used as a default value for a duplicate state variable in SearchFeature, and changes don't flow up to the parent:
// filteredToys has the real state. toysFiltered is just a default value.
const [filteredToys, setFilteredToys] = useState(toysFiltered);
Instead, leave the state variable in the parent, and send changes up from the search component.
Also, useMemo shouldn't have side effects. I've changed it to useEffect to be safe. There is a way to do this with useMemo, but that's out of the scope of the question.
in ToysPage.js
Add a setter function:
<SearchSide
toysFiltered={this.state.toysFiltered}
setToysFiltered={
newFilterList => this.setState({...this.state, toysFiltered: newFilterList})}
/>
Pass it through from SearchSide into SearchFeature:
function SearchSide({ toysFiltered, setToysFiltered }) {
return (
<>
<div className="col-3">
<SearchFeature toysFiltered={toysFiltered} setToysFiltered={setToysFiltered} />
</div>
</>
);
}
SearchFeature.js
export default function SearchFeature({ toysFiltered, setToysFiltered }) {
const [query, setQuery] = useState("");
useEffect(() => {
const result = toysFiltered.filter((toy) => {
return toy.titulo.toLowerCase().includes(query.toLowerCase());
});
setToysFiltered(result);
}, [setToysFiltered, query]);
// ...
}

Related

How to update the parent / list component from the child / detail component in ReactJS?

I am beginner and practicing on Library Management System in react. So I have components named BookDetails.js, BookList.js. BookDetails contains the form for entering Title and Description. So How can I pass the data entered from BookDetails to BookList and to dispaly from App.
import React, { useState } from 'react'
import BookList from './BookList'
const BookDetails = (props) => {
const [bookdetails, setbookDetails] = useState('')
const [desc, setDesc] = useState('')
const titleChangehandler = (e) => {
setbookDetails(e.target.value)
}
const descriptionChangehandler = (e) => {
setDesc(e.target.value)
}
const submitHandler = (e) => {
e.preventDefault()
return (
<div className='bookdetails'>
<form className='form_bookdetails' onSubmit={submitHandler}>
<div>
<label>Enter Title:</label>
<input type='text' value={bookdetails} onChange={titleChangehandler}></input>
</div>
<div>
<label>Enter Description:</label>
<input type='text' value={desc} onChange={descriptionChangehandler}></input>
</div>
<div>
<button type='submit'>Add Details</button>
</div>
</form>
</div>
)
}
}
export default BookDetails
BookList.js
import React from 'react'
import './BookList.css'
import BookDetails from './BookDetails'
const BookList = () => {
return (
<div className="booklist">
<header>BookList</header>
<BookDetails />
</div>
)
}
export default BookList
You need to use props. BookList state will have an update function that it will pass to the BookDetail via props. Example (CodeSandbox) with Todo with title & description.
BookDetail will invoke this method on every save which then would update the original list.
TodoList.js
export default function TodoList() {
const [todo, setTodo] = React.useState(null);
const [todoList, setTodoList] = React.useState([]);
React.useEffect(() => {
getTodos();
}, []);
function getTodos() {
console.log("===> fetch all todos!!");
fetchTodos().then((todos) => {
setTodoList(todos);
});
}
function editTodo(todo) {
console.log("===> set todo => ", todo);
setTodo(todo);
}
function handleUpdate(updatedTodo) {
// update Todo
const updatedTodos = todoList.map((el) =>
el.id === updatedTodo.id ? updatedTodo : el
);
setTodoList(updatedTodos);
setTodo(null);
}
return (
<div>
<ul>
{todoList.map((item) => (
<li key={item.id}>
{item.title}, {item.description}
<button onClick={() => editTodo(item)}>edit</button>
</li>
))}
</ul>
{todo && <TodoDetail todo={todo} updateTodo={handleUpdate} />}
</div>
);
}
TodoDetail.js
import React from "react";
export default function TodoDetail(props) {
const [todo, setTodo] = React.useState(props.todo);
console.log("todo =>", todo);
function handleChange(key, value) {
console.log("===> todo changed!");
setTodo({
...todo,
[key]: value
});
}
function handleSubmit() {
// api PUT on todo
console.log("===> todo edit submit!!");
props.updateTodo(todo);
}
return (
<div>
<form onSubmit={handleSubmit}>
<label htmlFor="title">
<input
value={todo.title}
onChange={(e) => handleChange("title", e.target.value)}
/>
<input
value={todo.description}
onChange={(e) => handleChange("description", e.target.value)}
/>
</label>
<button type="submit">submit</button>
</form>
</div>
);
}
You can store the list of books in your BookList component like
const [bookList, setBookList] = useState([])
This way your BookList component has access to the books. You can then create a function to add books to the list
function addBook(book) {
setBookList([...bookList, book])
}
Then pass the addBook() function to the BookDetails component to use it on submit.
<BookDetails addBook={addBook}
Now BookDetails can access the function as a prop
props.addBook("pass new book here")

Why it is rendering first and then updating the state later in react?

I want to fetch the data when the button is clicked but the Newsitem component is running first and then updating the value of data_grabber. That means it is displaying the defalut values rather than the data that I fetched from the newsapi. After displaying the newsitem component with default values, data_grabber is updating the fetched data.
What can be the solution?
App.js
function App() {
const [input_data, setInput_data] = useState("");
const [btn_data, setBtn_data] = useState("");
const [data_grabber, setData_grabber] = useState([]);
return (
<>
<Navbar
input_data={input_data}
setInput_data={setInput_data}
setBtn_data={setBtn_data}
btn_data={btn_data}
data_grabber={data_grabber}
setData_grabber={setData_grabber}
/>
{data_grabber? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
</>
);
}
export default App;
Navbar.js
import { useEffect } from "react";
export default function Navbar(props) {
const onClicker = async (e) => {
e.preventDefault();
props.setBtn_data(props.input_data);
};
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=API_KEY`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
props.setData_grabber(data.articles)
};
fetcher();
}, [props.btn_data]);
return (
<div>
<form className="d-flex">
<input
onChange={(e) => props.setInput_data(e.target.value)}
value={props.input_data}
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button
className="btn btn-outline-success"
type="submit"
onClick={onClicker}
>
Search
</button>
</form>
</div>
NewsItem.js
import React, { Component } from "react";
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.data
const defaultImage = `https://blogger.googleusercontent.com/img/a/AVvXsEh20SgNNsDlKyWWmB7XgB5SfFY10M6CqJAq93HwGtssTn2cWz6w9zHPjXf91WwoWr27QeaC4HsGv2NxPOXUdvk6xodUojnw8rUuAkEMY3Qb4ucoVpN3nSyF8JW_xVDWa2aSMEWH387hPsfouSJyClLNburIcDbXIeJamuTHwiSvw4hdNnqeeICcvg1wrQ=w1200-h630-p-k-no-nu`
return (
<div>
<div className="card">
<img src={urlToImage?urlToImage:defaultImage} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{title?title:'No title available'}</h5>
<p className="card-text">
{description?description.slice(0, 50):"no description available"}...
</p>
<a href={url} target="_blank" rel="noreferrer"className="btn btn-primary">
read more
</a>
</div>
</div>
</div>
);
}
}
One fix could be to
make a variable of the updated state:
in the UseEffect ,
add :
const updated = data.articles
props.setData_grabber(updated)
Check whether data_grabber array is empty or not and then do the rendering inside App component as follows.
{
data_grabber.length > 0 ? (
data_grabber.map((news_data) => {
return <NewsItem news_data={news_data} />;
})
) : (
<div>No data available</div>
);
}
{ data_grabber !== undefined && data_grabber.length > 0 ? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
Check data_grabber is undefined or empty.
Then, fix NewsItem props.data like this.
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.news_data
also fix here in useEffect
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=c990aa0235da4635997afd1f7459860c`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
if(data.articles){
props.setData_grabber(data.articles)
}
};
fetcher();

react useEffect with function received as props from parent component

I have a parent component
import React ,{useState} from 'react'
import ChildComponenet from './child'
export default function App() {
const [parentValue , setParentValue] = useState(0)
const someParentfunc = (input1,input2)=>{
setParentValue( parseInt(input1,10) +parseInt(input2,10))
}
return (
<div className="App">
Add : {parentValue}
<ChildComponenet someParentfunc={someParentfunc} />
</div>
);
}
now the child component :
import React ,{useState,useEffect} from 'react'
function ChildComponenet({someParentfunc}) {
const [input1 , setInput1] = useState(10)
const [input2 , setInput2] = useState(20)
useEffect(() => {
someParentfunc(input1,input2)
}, [input1,input2])
return (
<div>
<input type="text"
value={input1}
onChange={event => setInput1(event.target.value) }
/>
<input type="text"
value={input2}
onChange={event => setInput2(event.target.value) }
/>
</div>
)
}
export default ChildComponenet
the error is: React Hook useEffect has a missing dependency: 'someParentfunc'. Either include it or remove the dependency array.
should I add someParentfunc in the useEffect , will it be right approach :
useEffect(() => {
someParentfunc(input1, input2);
}, [input1, input2,someParentfunc]);
the working project link is : https://codesandbox.io/s/competent-kare-kce02?file=/src/child.js
someParentfunc is not using any value from it closures and so if you avoid it in in the dependency, there shouldn't be any problem. However for the purpose of ou code being extendable you should try to follow the patterns and add it in the dependenccy so that if doesn't cause any issues if the function is updated in future
To add it as a dependency you should make sure to use useCallback function so that the function is not re-created on each re-render
export default function App() {
const [parentValue , setParentValue] = useState(0)
const someParentfunc = useCallback((input1,input2)=>{
setParentValue( parseInt(input1,10) +parseInt(input2,10))
}, [setParentValue]);
return (
<div className="App">
Add : {parentValue}
<ChildComponenet someParentfunc={someParentfunc} />
</div>
);
}
Now you can add it to the useEffect dependenccy in child component
function ChildComponent({someParentfunc}) {
const [input1 , setInput1] = useState(10)
const [input2 , setInput2] = useState(20)
useEffect(() => {
someParentfunc(input1,input2)
}, [input1,input2, someParentfunc])
return (
<div>
<input type="text"
value={input1}
onChange={event => setInput1(event.target.value) }
/>
<input type="text"
value={input2}
onChange={event => setInput2(event.target.value) }
/>
</div>
)
}

Update state from deeply nested component without re-rendering parents

I have a form page structured more or less as follows:
<Layout>
<Page>
<Content>
<Input />
<Map />
</Content>
</Page>
<Button />
</Layout>
The Map component should only be rendered once, as there is an animation that is triggered on render. That means that Content, Page and Layout should not re-render at all.
The Button inside Layout should be disabled when the Input is empty. The value of the Input is not controlled by Content, as a state change would cause a re-render of the Map.
I've tried a few different things (using refs, useImperativeHandle, etc) but none of the solutions feel very clean to me. What's the best way to go about connecting the state of the Input to the state of the Button, without changing the state of Layout, Page or Content? Keep in mind that this is a fairly small project and the codebase uses "modern" React practices (e.g. hooks), and doesn't have global state management like Redux, MobX, etc.
Here is an example (click here to play with it) that avoids re-render of Map. However, it re-renders other components because I pass children around. But if map is the heaviest, that should do the trick. To avoid rendering of other components you need to get rid of children prop but that most probably means you will need redux. You can also try to use context but I never worked with it so idk how it would affect rendering in general
import React, { useState, useRef, memo } from "react";
import "./styles.css";
const GenericComponent = memo(
({ name = "GenericComponent", className, children }) => {
const counter = useRef(0);
counter.current += 1;
return (
<div className={"GenericComponent " + className}>
<div className="Counter">
{name} rendered {counter.current} times
</div>
{children}
</div>
);
}
);
const Layout = memo(({ children }) => {
return (
<GenericComponent name="Layout" className="Layout">
{children}
</GenericComponent>
);
});
const Page = memo(({ children }) => {
return (
<GenericComponent name="Page" className="Page">
{children}
</GenericComponent>
);
});
const Content = memo(({ children }) => {
return (
<GenericComponent name="Content" className="Content">
{children}
</GenericComponent>
);
});
const Map = memo(({ children }) => {
return (
<GenericComponent name="Map" className="Map">
{children}
</GenericComponent>
);
});
const Input = ({ value, setValue }) => {
const onChange = ({ target: { value } }) => {
setValue(value);
};
return (
<input
type="text"
value={typeof value === "string" ? value : ""}
onChange={onChange}
/>
);
};
const Button = ({ disabled = false }) => {
return (
<button type="button" disabled={disabled}>
Button
</button>
);
};
export default function App() {
const [value, setValue] = useState("");
return (
<div className="App">
<h1>SO Q#60060672</h1>
<Layout>
<Page>
<Content>
<Input value={value} setValue={setValue} />
<Map />
</Content>
</Page>
<Button disabled={value === ""} />
</Layout>
</div>
);
}
Update
Below is version with context that does not re-render components except input and button:
import React, { useState, useRef, memo, useContext } from "react";
import "./styles.css";
const ValueContext = React.createContext({
value: "",
setValue: () => {}
});
const Layout = memo(() => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Layout rendered {counter.current} times</div>
<Page />
<Button />
</div>
);
});
const Page = memo(() => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Page rendered {counter.current} times</div>
<Content />
</div>
);
});
const Content = memo(() => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Content rendered {counter.current} times</div>
<Input />
<Map />
</div>
);
});
const Map = memo(() => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Map rendered {counter.current} times</div>
</div>
);
});
const Input = () => {
const { value, setValue } = useContext(ValueContext);
const onChange = ({ target: { value } }) => {
setValue(value);
};
return (
<input
type="text"
value={typeof value === "string" ? value : ""}
onChange={onChange}
/>
);
};
const Button = () => {
const { value } = useContext(ValueContext);
return (
<button type="button" disabled={value === ""}>
Button
</button>
);
};
export default function App() {
const [value, setValue] = useState("");
return (
<div className="App">
<h1>SO Q#60060672, method 2</h1>
<p>
Type something into input below to see how rendering counters{" "}
<s>update</s> stay the same
</p>
<ValueContext.Provider value={{ value, setValue }}>
<Layout />
</ValueContext.Provider>
</div>
);
}
Solutions rely on using memo to avoid rendering when parent re-renders and minimizing amount of properties passed to components. Ref's are used only for render counters
I have a sure way to solve it, but a little more complicated.
Use createContext and useContext to transfer data from layout to input. This way you can use a global state without using Redux. (redux also uses context by the way to distribute its data). Using context you can prevent property change in all the component between Layout and Imput.
I have a second easier option, but I'm not sure it works in this case. You can wrap Map to React.memo to prevent render if its property is not changed. It's quick to try and it may work.
UPDATE
I tried out React.memo on Map component. I modified Gennady's example. And it works just fine without context. You just pass the value and setValue to all component down the chain. You can pass all property easy like: <Content {...props} /> This is the easiest solution.
import React, { useState, useRef, memo } from "react";
import "./styles.css";
const Layout = props => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Layout rendered {counter.current} times</div>
<Page {...props} />
<Button {...props} />
</div>
);
};
const Page = props => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Page rendered {counter.current} times</div>
<Content {...props} />
</div>
);
};
const Content = props => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Content rendered {counter.current} times</div>
<Input {...props} />
<Map />
</div>
);
};
const Map = memo(() => {
const counter = useRef(0);
counter.current += 1;
return (
<div className="GenericComponent">
<div className="Counter">Map rendered {counter.current} times</div>
</div>
);
});
const Input = ({ value, setValue }) => {
const counter = useRef(0);
counter.current += 1;
const onChange = ({ target: { value } }) => {
setValue(value);
};
return (
<>
Input rendedred {counter.current} times{" "}
<input
type="text"
value={typeof value === "string" ? value : ""}
onChange={onChange}
/>
</>
);
};
const Button = ({ value }) => {
const counter = useRef(0);
counter.current += 1;
return (
<button type="button" disabled={value === ""}>
Button (rendered {counter.current} times)
</button>
);
};
export default function App() {
const [value, setValue] = useState("");
return (
<div className="App">
<h1>SO Q#60060672, method 2</h1>
<p>
Type something into input below to see how rendering counters{" "}
<s>update</s> stay the same, except for input and button
</p>
<Layout value={value} setValue={setValue} />
</div>
);
}
https://codesandbox.io/s/weathered-wind-wif8b

React Hook : Send data from child to parent component

I'm looking for the easiest solution to pass data from a child component to his parent.
I've heard about using Context, pass trough properties or update props, but I don't know which one is the best solution.
I'm building an admin interface, with a PageComponent that contains a ChildComponent with a table where I can select multiple line. I want to send to my parent PageComponent the number of line I've selected in my ChildComponent.
Something like that :
PageComponent :
<div className="App">
<EnhancedTable />
<h2>count 0</h2>
(count should be updated from child)
</div>
ChildComponent :
const EnhancedTable = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Click me {count}
</button>
)
};
I'm sure it's a pretty simple thing to do, I don't want to use redux for that.
A common technique for these situations is to lift the state up to the first common ancestor of all the components that needs to use the state (i.e. the PageComponent in this case) and pass down the state and state-altering functions to the child components as props.
Example
const { useState } = React;
function PageComponent() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1)
}
return (
<div className="App">
<ChildComponent onClick={increment} count={count} />
<h2>count {count}</h2>
(count should be updated from child)
</div>
);
}
const ChildComponent = ({ onClick, count }) => {
return (
<button onClick={onClick}>
Click me {count}
</button>
)
};
ReactDOM.render(<PageComponent />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
You can create a method in your parent component, pass it to child component and call it from props every time child's state changes, keeping the state in child component.
const EnhancedTable = ({ parentCallback }) => {
const [count, setCount] = useState(0);
return (
<button onClick={() => {
const newValue = count + 1;
setCount(newValue);
parentCallback(newValue);
}}>
Click me {count}
</button>
)
};
class PageComponent extends React.Component {
callback = (count) => {
// do something with value in parent component, like save to state
}
render() {
return (
<div className="App">
<EnhancedTable parentCallback={this.callback} />
<h2>count 0</h2>
(count should be updated from child)
</div>
)
}
}
To make things super simple you can actually share state setters to children and now they have the access to set the state of its parent.
example:
Assume there are 4 components as below,
function App() {
return (
<div className="App">
<GrandParent />
</div>
);
}
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
return (
<>
<div>{name}</div>
<Parent setName={setName} />
</>
);
};
const Parent = params => {
return (
<>
<button onClick={() => params.setName("i'm from Parent")}>
from Parent
</button>
<Child setName={params.setName} />
</>
);
};
const Child = params => {
return (
<>
<button onClick={() => params.setName("i'm from Child")}>
from Child
</button>
</>
);
};
so grandparent component has the actual state and by sharing the setter method (setName) to parent and child, they get the access to change the state of the grandparent.
you can find the working code in below sandbox,
https://codesandbox.io/embed/async-fire-kl197
IF we Have Parent Class Component and Child function component this is how we going to access child component useStates hooks value :--
class parent extends Component() {
constructor(props){
super(props)
this.ChildComponentRef = React.createRef()
}
render(){
console.log(' check child stateValue: ',
this.ChildComponentRef.current.info);
return (<> <ChildComponent ref={this.ChildComponentRef} /> </>)
}
}
Child Component we would create using
React.forwardRef((props, ref) => (<></>))
. and
useImperativeHandle(ref, createHandle, [deps])
to customizes the instance value that is exposed to parent components
const childComponent = React.forwardRef((props, ref) => {
const [info, setInfo] = useState("")
useEffect(() => {
axios.get("someUrl").then((data)=>setInfo(data))
})
useImperativeHandle(ref, () => {
return {
info: info
}
})
return (<> <h2> Child Component <h2> </>)
})
I had to do this in type script. The object-oriented aspect would need the dev to add this callback method as a field in the interface after inheriting from parent and the type of this prop would be Function. I found this cool!
Here's an another example of how we can pass state directly to the parent.
I modified a component example from react-select library which is a CreatableSelect component. The component was originally developed as class based component, I turned it into a functional component and changed state manipulation algorithm.
import React, {KeyboardEventHandler} from 'react';
import CreatableSelect from 'react-select/creatable';
import { ActionMeta, OnChangeValue } from 'react-select';
const MultiSelectTextInput = (props) => {
const components = {
DropdownIndicator: null,
};
interface Option {
readonly label: string;
readonly value: string;
}
const createOption = (label: string) => ({
label,
value: label,
});
const handleChange = (value: OnChangeValue<Option, true>, actionMeta: ActionMeta<Option>) => {
console.group('Value Changed');
console.log(value);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
props.setValue(value);
};
const handleInputChange = (inputValue: string) => {
props.setInputValue(inputValue);
};
const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
if (!props.inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
console.group('Value Added');
console.log(props.value);
console.groupEnd();
props.setInputValue('');
props.setValue([...props.value, createOption(props.inputValue)])
event.preventDefault();
}
};
return (
<CreatableSelect
id={props.id}
instanceId={props.id}
className="w-100"
components={components}
inputValue={props.inputValue}
isClearable
isMulti
menuIsOpen={false}
onChange={handleChange}
onInputChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder="Type something and press enter..."
value={props.value}
/>
);
};
export default MultiSelectTextInput;
I call it from the pages of my next js project like this
import MultiSelectTextInput from "../components/Form/MultiSelect/MultiSelectTextInput";
const NcciLite = () => {
const [value, setValue] = useState<any>([]);
const [inputValue, setInputValue] = useState<any>('');
return (
<React.Fragment>
....
<div className="d-inline-flex col-md-9">
<MultiSelectTextInput
id="codes"
value={value}
setValue={setValue}
inputValue={inputValue}
setInputValue={setInputValue}
/>
</div>
...
</React.Fragment>
);
};
As seen, the component modifies the page's (parent page's) state in which it is called.
I've had to deal with a similar issue, and found another approach, using an object to reference the states between different functions, and in the same file.
import React, { useState } from "react";
let myState = {};
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
myState.name=name;
myState.setName=setName;
return (
<>
<div>{name}</div>
<Parent />
</>
);
};
export default GrandParent;
const Parent = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Parent")}>
from Parent
</button>
<Child />
</>
);
};
const Child = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Child")}>
from Child
</button>
</>
);
};

Resources