I am trying to reducing my code complexity to express by defining just skeleton code bellow. have to trigger the toggleModel of the child component
import React, { useState } from "react";
import "./styles.css";
const ChildComponent = (props) => {
// .... some useStates
const toggleModel = () => {
// have to trigger this methoud once user clicks on button
// have to change some states here
};
return (
<div>
{props.children}
...... other things .......
</div>
);
};
export default function ParentComponet() {
return (
<div className="App">
Hello
<ChildComponent>
<button
type="button"
onClick={() => {
// here i have to trigger the toggleModel function of ChildComponent
}}
>
Toggle Model
</button>
</ChildComponent>
</div>
);
}
i am rendering child component by sending children elements, have to trigger the toggleModel of the child component it will reduce my 70 % redundant code at our application. is there any way to achieve the same codesandbox. Thank you in advance
You can use useState and useEffect to pass state down and react to it.
import React, { useState } from "react";
import "./styles.css";
const ChildComponent = ({visible, children, setVisible}) => {
React.useEffect(() => {
const toggleModel = () => {
alert('Visible changes to ' + visible )
};
toggleModel()
}, [visible])
return <div>{children}</div>;
};
export default function ParentComponet() {
const [visible, setVisible] = React.useState(false)
return (
<div className="App">
Hello
<ChildComponent visible={visible} setVisible={setVisible}>
<button
type="button"
onClick={()=> setVisible(!visible)}
>
Toggle Model
</button>
</ChildComponent>
</div>
);
}
https://codesandbox.io/s/objective-ramanujan-j3eqg
The alternative is use #yaiks answer.
You can take a look at this question here, it can help you.
But I would say it's not a good practice to call a child function from the parent. Usually what I would do is to "lift up" the method to the parent, and pass down to the child if possible.
Here is another way to call your ChilComponent's function - using forwardRef:
import React, { useState, useImperativeHandle, forwardRef } from "react";
import "./styles.css";
const ChildComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
toggleModel() {
alert("alert from ChildComponent");
}
}));
return <div>{props.children}</div>;
});
export default function ParentComponet() {
return (
<div className="App">
Hello
<ChildComponent ref={ChildComponent}>
<button
type="button"
onClick={() => ChildComponent.current.toggleModel()}
>
Toggle Model
</button>
</ChildComponent>
</div>
);
}
Sandbox: https://codesandbox.io/s/pensive-jones-lw0pf?file=/src/App.js
My answer is courtesy of #rossipedia: https://stackoverflow.com/a/37950970/1927991
Related
I'm passing the value of my state using useContext. However it keeps on saying that "toggle" is undefined how do i solved this part that the value of toggle can be passed to mainSection?
Here's my code
import React, { useState, createContext } from 'react';
import ReactDOM from 'react-dom';
const languages = ['JavaScript', 'Python'];
export const reactContext = React.createContext()
function App() {
const [toggle,setToggle] = useState(false)
// implement Context here so can be used in child components
return (
<reactContext.Provider value={{toggle,setToggle}}>
<MainSection />
</reactContext.Provider >
);
}
function MainSection() {
console.log(toggle)
return (
<div>
<p id="favoriteLanguage">Favorite programing language: {languages[0]}</p>
<button id="changeFavorite" onClick={() => console.log(toggle)}>Toggle language</button>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
you are missing useContext. At the same time you should likely use ReactContext instead of reactContext.
function MainSection() {
const { toggle, setToggle } = useContext(reactContext) // <-- you are missing this line
console.log(toggle)
return (
<div>
<p id="favoriteLanguage">Favorite programing language: {languages[0]}</p>
<button id="changeFavorite" onClick={() => console.log(toggle)}>Toggle language</button>
</div>
)
}
Docs - https://reactjs.org/docs/hooks-reference.html#usecontext
I'm trying to give one component the array value at a certain index and assign a value that i want from the child component.
I want it like this because I'm trying to do a survey app and the number of question can be different. this is just a little test that concludes what I want.
the base Component
import React, { useState } from 'react';
import './style.css';
import Comp from './Component.js';
export default function App() {
const [results, setResults] = useState([]);
results.length = 20;
results[3] = 'kiss';
results[2] = [12, 454, 45];
console.log(results);
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<Comp result={results[5]}/>
<button onClick={() => console.log(results)}> SHOW </button>
</div>
);
}
the component
import React, { useState } from 'react';
const Comp = ({result}) => {
result = 1
console.log(result)
return (
<div>
hhhhh
</div>
);
}
export default Comp
here is a place I set it up => https://stackblitz.com/edit/react-mfpk5f?file=src%2FApp.js,src%2FComponent.js
every suggestion is highly appreciated!
parent componentHere i have tried a way to find solution ,
just keep a callback function in child component and call a function of parent component inside child so that u can pass data to it .
child component
If you want to add more, you use the setResults() e.g. setResults(['kiss']); so now your results='kiss', if you want more is setResults(...results,[12, 454, 45]); and now your results= kiss,12,454,45 . But:
import React, { useState } from 'react';
import './style.css';
import Comp from './Component.js';
export default function App() {
const [results, setResults] = useState(['hiii']);
function handleClick() {
const array1 = 'kiss';
const array2 = [12, 454, 45];
setResults([...results, array1, array2]);
console.log(results);
}
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<Comp result={results[5]} />
<button onClick={() => handleClick()}> SHOW </button>
</div>
);
}
First you need to add the values when something happened, e.g. onClick={...}.
<Comp result={results[5]}/> this is correct, but you call when the result=[] show you need to call after updating, e.g.
import React, { useState } from 'react';
import './style.css';
import Comp from './Component.js';
export default function App() {
const [results, setResults] = useState(['Hi']);
function handleClick() {
const array1 = 'kiss';
const array2 = [12, 454, 45];
setResults([...results, array1]);
console.log(results);
}
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<Comp result={results} />
<button onClick={() => handleClick()}> {results} </button>
<div>
{results.map(() => {
return <Comp result={results[5]} />;
})}
</div>
</div>
);
}
Of course this is NOT the best solution, but I hope you understand what happened, and like you will see you need to press the button 5 times to get something for the results[5] <Comp result={results[5]} />
and for the last you need to change the Comp:
import React, { useState } from 'react';
const Comp = ({result}) => {
const [compResults, setcompResults] = useState(result);
console.log(compResults)
return (
<div>
{compResults}
</div>
);
}
export default Comp
I'm new to React and am attempting to set up a Bootstrap modal to show alert messages.
In my parent App.js file I have an error handler that sends a Modal.js component a prop that triggers the modal to show, eg:
On App.js:
function App() {
const [modalShow, setModalShow] = useState(false);
// Some other handlers
const alertModalHandler = (modalMessage) => {
console.log(modalMessage);
setModalShow(true);
}
return (
// Other components.
<AlertModal modalOpen={modalShow}/>
)
}
And on Modal.js:
import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import "bootstrap/dist/css/bootstrap.min.css";
const AlertModal = (props) => {
const [isOpen, setIsOpen] = useState(false);
if (props.modalOpen) {
setIsOpen(true);
}
return (
<Modal show={isOpen}>
<Modal.Header closeButton>Hi</Modal.Header>
<Modal.Body>asdfasdf</Modal.Body>
</Modal>
);
};
export default AlertModal;
However, this doesn't work. I get the error:
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
If I change the Modal component to be a 'dumb' component and use the prop directly, eg:
const AlertModal = (props) => {
return (
<Modal show={props.modalOpen}>
<Modal.Header closeButton>Hi</Modal.Header>
<Modal.Body>asdfasdf</Modal.Body>
</Modal>
);
};
It does work, but I was wanting to change the show/hide state on the Modal.js component level as well, eg have something that handles modal close buttons in there.
I don't understand why is this breaking?
And does this mean I will have to handle the Modal close function at the parent App.js level?
Edit - full app.js contents
import React, { useState } from 'react';
import './App.css';
import 'bootstrap/dist/css/bootstrap.css';
import AddUserForm from './components/addUserForm';
import UserList from './components/userList';
import AlertModal from './components/modal';
function App() {
const [users, setUsers] = useState([]);
const [modalShow, setModalShow] = useState(false);
const addPersonHandler = (nameValue, ageValue) => {
console.log(nameValue, ageValue);
setUsers(prevUsers => {
const updatedUsers = [...prevUsers];
updatedUsers.unshift({ name: nameValue, age: ageValue });
return updatedUsers;
});
};
const alertModalHandler = (modalMessage) => {
console.log(modalMessage);
setModalShow(true);
}
let content = (
<p style={{ textAlign: 'center' }}>No users found. Maybe add one?</p>
);
if (users.length > 0) {
content = (
<UserList items={users} />
);
}
return (
<>
<div className="container">
<div className="row">
<div className="col-md-6 offset-md-3">
<AddUserForm onAddPerson={addPersonHandler} fireAlertModal={alertModalHandler}/>
</div>
</div>
<div className="row">
<div className="col-md-6 offset-md-3">
{content}
</div>
</div>
</div>
<AlertModal modalOpen={modalShow}/>
</>
);
}
export default App;
In your modal.js
you should put
if (props.modalOpen) {
setIsOpen(true);
}
in a useEffect.
React.useEffect(() => {if (props.modalOpen) {
setIsOpen(true);
}}, [props.modalOpen])
You should never call setState just like that. If you do it will run on every render and trigger another render, because you changed the state. You should put the setModalShow together with the if clause in a useEffect. E.g.:
useState(() => {
if (modalOpen) {
setIsOpen(true);
}
}, [modalOpen])
Note that I also restructered modalOpen out of props. That way the useEffect will only run when modalOpen changes.
If you already send a state called modalShow to the AlertModal component there is no reason to use another state which does the same such as isOpen.
Whenever modalShow is changed, it causes a re-render of the AlertModal component since you changed it's state, then inside if the prop is true you set another state, causing another not needed re-render when you set isOpen. Then, on each re-render if props.showModal has not changed (and still is true) you trigger setIsOpen again and again.
If you want control over the modal open/close inside AlertModal I would do as follows:
<AlertModal modalOpen={modalShow} setModalOpen={setModalShow}/>
Pass the set function of the showModal state to the modal component, and there use it as you see fit. For example, in an onClick handler.
modal.js:
import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import "bootstrap/dist/css/bootstrap.min.css";
const AlertModal = (props) => {
const onClickHandler = () => {
props.setModalOpen(prevState => !prevState)
}
return (
<Modal show={props.modalOpen}>
<Modal.Header closeButton>Hi</Modal.Header>
<Modal.Body>asdfasdf</Modal.Body>
</Modal>
);
};
export default AlertModal;
I am trying to trigger a start function in a different componentB when I click the start button in componentA
Note: Both components are neither parent to child components
Component A
import React from "react"
function ComponentA(props) {
return (
<div>
<button>Start</button>
</div>
)
}
export default ComponentA;
Component B
import React from "react";
function ComponentB(props) {
const [isStarted, setStarted] = React.useState(false);
const start = () => setStarted(true);
return <div>{isStarted ? "Starting..." : "Not Starting.."}</div>;
}
export default ComponentB;
One way you could do it is by creating a callback prop on ComponentA, changing the state of the parent component of ComponentA and passing it to ComponentB via a prop and capture that prop change with a useEffect.
Example:
Parent
function Parent(){
const [started, setStarted] = useState(false)
return(
<div>
<ComponentA onClick={() => setStarted(true)}/>
<ComponentB started={started}/>
</div>
)
}
ComponentA
function ComponentA({onClick}){
return(
<div>
<button onClick={() => onClick()}/>
</div>
)
}
ComponentB
function ComponentB({started}) {
const [isStarted, setStarted] = React.useState(started);
useEffect(() => {
setStarted(started)
}, [started])
return <div>{isStarted ? "Starting..." : "Not Starting.."}</div>;
}
Another way would be using useContext:
https://reactjs.org/docs/hooks-reference.html#usecontext
https://reactjs.org/docs/context.html
Honestly, I am a bit lazy to also include an example which is in my opinion worse. Here is an example that uses useContext that might be useful.
https://stackoverflow.com/a/54738889/7491597
I currently have my Parent set up as follows, which I'm then passing props to
class WorkoutPlan extends React.Component {
constructor() {
super();
this.state = {
workoutPlan: {}
};
}
componentDidMount() {
axios
.get("/api/workout-plan")
.then(response => {
this.setState({ workoutPlan: response.data });
})
.catch(error => {
console.log(error);
});
}
render() {
const { workoutPlan } = this.state;
// const workoutPlan = this.state.workoutPlan;
return (
<div>
<h1>{workoutPlan.Name}</h1>
<button className="button" onClick={this.handleClick}>
Click Me
</button>
<Workout {...workoutPlan.workout} />
</div>
);
}
}
Then in my child, I'm wanting to pass those same props to another Child
import React from "react";
import Exercise from "./Exercise";
const Workout = props => {
return (
<div>
<h2>"Workout for {props.day}"</h2>
<Exercise {...workoutPlan.workout} />
</div>
);
};
export default Workout;
I can't seem to figure out how I would go about doing this. I'm being told that the setup is exactly the same as the 1st child, but when I enter in the same code, it's not working.
You can pass {...props} to your Exercise component so your Workout component should look like this
import React from "react";
import Exercise from "./Exercise";
const Workout = props => {
return (
<div>
<h2>"Workout for {props.day}"</h2>
<Exercise {...props} />
</div>
);
};
export default Workout;
When you pass props destructuring it, the effect it's the same as you were passing props one by one.
You can't achieve your goal because in your Workout component there is no "workout" prop.
Try to pass props to Exercise component like this:
<Exercise {...props} />