Setting state from another component react hooks - javascript

There a two components one is <Form /> and other is <LoginForm />.
<LoginForm /> looks like
const LoginForm = () => {
return (
<Form
inputs={[
//some objects here
]}
onSubmit={(data, setError) => {
setError('some error')
}}
/>
);
};
The <Form /> component looks like.
const Form = ({onSumbit, inputs}) => {
const [error, setError] = useState('');
return (
<>
{error ? <div>{error}</div> : null}
{//rendering inputs here}
<button onClick={() => onSubmit('some data which is not relative to problem', setError)}>
</>
)
}
Now when the button is clicked on onSubmit() should run. And it should call setError which should show some error but its not showing any error. Its also not showing any kind of error.
Note: This is only the relevant part of code. Code is actually large. But I am sure that only this part have some basic flaw.

Sometimes you declared onSumbit and sometimes onSubmit, you need to be consistent:
const LoginForm = () => {
return (
<Form
onSubmit={(data, setter) => {
setter('some error');
}}
/>
);
};
const Form = ({ onSubmit, inputs }) => {
const [error, setError] = useState('');
return (
<>
{error ? <div>{error}</div> : null}
<button
onClick={() => {
onSubmit('some data which is not relative to problem', setError);
}}
>
Submit
</button>
</>
);
};

Here is my code. It works fine. Maybe there is some typo in your code.
const Form = ({onSubmit, inputs}) => {
const [error, setError] = useState('');
return (
<>
{error ? <div>{error}</div> : null}
<button onClick={() => onSubmit('some data which is not relative to problem', setError)}>
Hi
</button>
</>
)
}
const LoginForm = () => {
return (
<Form
inputs={[
//some objects here
]}
onSubmit={(data, setError) => {
setError('some error')
}}
/>
);
};

You had typos and syntax errors previously so this couldn't have worked for you anyway.
This seems to do what you meant.
const Form = ({ onSubmit, inputs }) => {
const [error, setError] = useState('');
return (
<>
{error ? <div>{error}</div> : null}
{// rendering inputs here
}
<button onClick={() => onSubmit('data', setError)} />
</>
);
};

Related

Warning: Cannot update a component (`Home`) while rendering a different component (`Posts`). To locate the bad setState() call inside `Posts`,

Problem: when i click on the button
<button
onClick={() => {
navigate('/posts');
setResponse(e.id);
}}
>
I get this error: Warning: Cannot update a component (Home) while rendering...
I think problem only in this line
navigate('/posts');
because if I delete it, error disappears
full code under without import
App.js
function App() {
const [response, setResponse] = useState({});
return (
<Router>
<Routes>
<Route exact path="/" element={<Home setResponse={setResponse} />} />
<Route exact path="/posts" element={<Posts response={response} />} />
<Route exact path="*" />
</Routes>
</Router>
);
}
export default App;
Home.js
function Home({ setResponse }) {
const [modal, setModal] = useState(false);
const navigate = useNavigate();
const dispatch = useDispatch();
const state = useSelector((state) => state);
console.log(state);
if (state.user.isLoading) {
return <h1>Loading...</h1>;
}
const toggleModal = () => {
setModal(!modal);
};
return (
<div className="App">
<button onClick={(e) => dispatch(fetchUsers())}>Fetch users</button>
{state.user.data &&
state.user.data.map((e) => (
<div key={e.id}>
<li key={e.name}>{e.name}</li>
<button
key={e.id + 10}
onClick={() => {
navigate('/posts');
setResponse(e.id);
}}
className="btn"
>
Posts
</button>
<button onClick={toggleModal} key={e.id + 100} className="bnt">
Albums
</button>
</div>
))}
<Albums modal={modal} setModal={setModal} />
</div>
);
}
export default Home;
Posts.js
function Posts({ response }) {
const dispatch = useDispatch();
const navigate = useNavigate();
const state = useSelector((state) => state);
console.log(state);
if (state.post.isLoading) {
return <h1>Loading...</h1>;
}
if (!state.post.data) {
dispatch(fetchPosts());
}
return (
<div className="App">
Posts
{state.post.data &&
state.post.data
.filter((e) => e.userId === response)
.map((e) => (
<div key={e.userId.toString() + e.id.toString()}>
<li key={e.id}>{e.title}</li>
</div>
))}
<button
onClick={() => {
navigate('/');
}}
>
List of users
</button>
</div>
);
}
export default Posts;
I tried to use useEffect(), but it doesn't work in my case
<button
onClick={() => {useEffect(()=>{
navigate('/posts');
setResponse(e.id);},[])
}}
>
If you're navigating away to another page, you shouldn't be updating the state as you're navigating (which you're doing by calling setResponse after navigate).
To fix this error, you'd have to call navigate() after React finishes updating the response variable, which you can do by using a useEffect call at the top-level of your Home component:
// you should also pass the value of response to the Home component so it knows when it's been changed
function Home({ setResponse, response }) {
const [modal, setModal] = useState(false);
const navigate = useNavigate();
const dispatch = useDispatch();
const state = useSelector((state) => state);
console.log(state);
useEffect(() => {
// This if statement checks if response === {}, which is a bit awkward; you should instead initialize your response state to a value like `null`
if (typeof response === 'object' && Object.keys(response).length === 0) {
return
}
navigate('/posts')
}, [response])
if (state.user.isLoading) {
return <h1>Loading...</h1>;
}
const toggleModal = () => {
setModal(!modal);
};
return (
<div className="App">
<button onClick={(e) => dispatch(fetchUsers())}>Fetch users</button>
{state.user.data &&
state.user.data.map((e) => (
<div key={e.id}>
<li key={e.name}>{e.name}</li>
<button
key={e.id + 10}
onClick={() => {
// navigate('/posts');
setResponse(e.id);
}}
className="btn"
>
Posts
</button>
<button onClick={toggleModal} key={e.id + 100} className="bnt">
Albums
</button>
</div>
))}
<Albums modal={modal} setModal={setModal} />
</div>
);
}
export default Home;

Array length doesn't change

i've got a problem. I'm trying to make data search from API.
The problem is that {people.length === 0 && <p>No data</p>} is not working. When i console.log people.length the length value doesn't change when i'm typing. Where's the problem?
Here's my code:
const PeopleSearch = () => {
const [people, setPeople] = useState([]);
const [search, setSearch] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
const fetchData = async () => {
const response = await axios.get(swapi);
const transformedPeople = response.data.results.sort((a, b) =>
a.name.localeCompare(b.name)
);
setPeople(transformedPeople);
setLoading(false);
};
fetchData();
}, []);
const searchHandler = (event) => {
setSearch(event.target.value);
};
return (
<>
<Input
type="text"
placeholder="Search by name..."
onChange={searchHandler}
/>
<section>
{loading ? (
<Loading />
) : (
<PeopleList
people={people.filter(({ name }) => {
if (name.toLowerCase().includes(search.toLowerCase())) {
return people;
}
})}
/>
)}
{people.length === 0 && <p>No data</p>}
</section>
</>
);
};
You're only calling setPeople once, on mount, with the useEffect(() => { ... }, []) - so people.length will remain the same once the API call completes. You need to use the filtered array both for the PeopleList prop and the No data check.
You should also tweak your filter, since it only cares about whether its return value is truthy or not.
const filteredPeople = people.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase()));
return (
<>
<Input
type="text"
placeholder="Search by name..."
onChange={searchHandler}
/>
<section>
{loading ? (
<Loading />
) : (
<PeopleList
people={filteredPeople}
/>
)}
{filteredPeople.length === 0 && !loading && <p>No data</p>}
</section>
</>
);

on click function not working because its in another return statement

I am trying to make history by pushing on button click
onclick function of the li is not working
as u can see in the code <SuggestionsList/> is in the last return statement its funtions are rendered in const SuggestionsList = (props) => { . The onclick funtion is comimg inside the const SuggestionsList funtion, this is making the onclick funtion not working
i created the exact working in codesand and its working there without any problem i dont get why its not working in my local
enter link description here
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<>
<li
style={styles.listyle}
onClick={finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
Instead of onClick={finddoctor(suggestion.id)} (Here just function invocation is happening and expected to have the callback method)
should be
onClick={() => finddoctor(suggestion.id)}

React Toggle Hook State

I tried to toggle individual item but unfortunately whenever I try to toggle an item the other item gets affected. Here is my code:
const FAQ = () => {
const [open, setOpen] = useState(false);
const [data, setData] = useState(faqData);
return (
<FAQSection>
<FAQTitle>Frequently Asked Questions</FAQTitle>
<Questions>
<QuestionItemDetail>
{data.map((item) => {
const { id, question, answer } = item;
return (
<div key={id}>
<QuestionItem onClick={() => setOpen(!open)}>
<QuestionItemTitle>{question}</QuestionItemTitle>
{open ? <Close /> : <Add />}
</QuestionItem>
<ReadQuestion>
{open && (
<ReadQuestionDetail>
<ReadQuestionDesc>{answer}</ReadQuestionDesc>
</ReadQuestionDetail>
)}
</ReadQuestion>
</div>
);
})}
</QuestionItemDetail>
</Questions>
</FAQSection>
);
};
What might be wrong with this because I ensured the dummy data has a unique ID.
Because you use a boolean to control all open/close. You need to use index/id to control this.
const [open, setOpen] = useState(null);
...
onClick={() => setOpen(preOpen => preOpen === id ? null : id)}
...
{open === id && (<ReadQuestionDetail>...</ReadQuestionDetail>)}
Your open state is used for all of the items in your data array, which is why it affects all of the items when toggled.
I recommend:
putting all of the data item html/jsx inside a new component.
Inside this new component, create an open state like so:
const MyItemComponent = (id, question, answer) => {
const [open, setOpen] = useState(false);
return (
<div key={id}>
<QuestionItem onClick={() => setOpen(!open)}>
<QuestionItemTitle>{question}</QuestionItemTitle>
{open ? <Close /> : <Add />}
</QuestionItem>
<ReadQuestion>
{open && (
<ReadQuestionDetail>
<ReadQuestionDesc>{answer}</ReadQuestionDesc>
</ReadQuestionDetail>
)}
</ReadQuestion>
</div>
);
}
const FAQ = () => {
const [data, setData] = useState(faqData);
return (
<FAQSection>
<FAQTitle>Frequently Asked Questions</FAQTitle>
<Questions>
<QuestionItemDetail>
{data.map((item) => {
const { id, question, answer } = item;
return (
<MyItemComponent id={id} question={question} answer={answer} />
);
})}
</QuestionItemDetail>
</Questions>
</FAQSection>
);
};
This will give you an individual open state for each item.

How to pass data to {props.children}

On my follow up question from here : How to pass data from child to parent component using react hooks
I have another issue.
Below is the component structure
export const Parent: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const createContent = (): JSX.Element => {
return (
<Authorization>
{<ErrorPanel message={errorMessage} setDisabled={setDisabled}/>}
<MyChildComponent/>
</<Authorization>
);
}
return (
<Button onClick={onSubmit} disabled={disabled}>My Button</Button>
{createContent()}
);
};
const Authorization: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const render = (errorMessage : JSX.Element): JSX.Element => {
return (
<>
{<ErrorPanel message={errorMessage} setDisabled={setDisabled}/>}
</>
);
};
return (
<>
<PageLoader
queryResult={apiQuery}
renderPage={render}
/>
{props.children}
</>
);
};
How do I pass the disabled state value from Authorization component to my child which is invoked by
{props.children}
I tried React.cloneElement & React.createContext but I'm not able to get the value disabled to the MyChildComponent. I could see the value for disabled as true once the errorMessage is set through the ErrorPanel in the Authorization component.
Do I need to have React.useEffect in the Authorization Component?
What am I missing here?
You need to use React.Children API with React.cloneElement:
const Authorization = ({ children }) => {
const [disabled, setDisabled] = React.useState(false);
const render = (errorMessage) => {
return (
<>{<ErrorPanel message={errorMessage} setDisabled={setDisabled} />}</>
);
};
return (
<>
<PageLoader queryResult={apiQuery} renderPage={render} />
{React.Children.map(children, (child) =>
React.cloneElement(child, { disabled })
)}
</>
);
};
// |
// v
// It will inject `disabled` prop to every component's child:
<>
<ErrorPanel
disabled={disabled}
message={errorMessage}
setDisabled={setDisabled}
/>
<MyChildComponent disabled={disabled} />
</>
You can make use of React.cloneElement to React.Children.map to pass on the disabled prop to the immediate children components
const Authorization: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const render = (errorMessage : JSX.Element): JSX.Element => {
return (
<>
{<ErrorPanel message={errorMessage} setDisabled={setDisabled}/>}
</>
);
};
return (
<>
<PageLoader
queryResult={apiQuery}
renderPage={render}
/>
{React.Children.map(props.children, child => {
return React.cloneElement(child, { disabled })
})}
</>
);
};
UPDATE:
Since you wish to update the parent state to, you should store the state and parent and update it there itself, instead of storing the state in child component too.
export const Parent: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const createContent = (): JSX.Element => {
return (
<Authorization setDisabled={setDisabled}>
{<ErrorPanel message={errorMessage} disabled={disabled} setDisabled={setDisabled}/>}
<MyChildComponent disabled={disabled}/>
</<Authorization>
);
}
return (
<Button onClick={onSubmit} disabled={disabled}>My Button</Button>
{createContent()}
);
};
const Authorization: React.FC<Props> = (props) => {
const render = (errorMessage : JSX.Element): JSX.Element => {
return (
<>
{<ErrorPanel message={errorMessage} disabled={props.disabled} setDisabled={props.setDisabled}/>}
</>
);
};
return (
<>
<PageLoader
queryResult={apiQuery}
renderPage={render}
/>
{props.children}
</>
);
};

Categories

Resources