How to Call function in loaded component in react from main component? - javascript

I wanted to toggle a div in loaded component that div in the same component but the Button for call the function in my main layout component hoe to fix this ? Please help
required button in In Main Component Landing.js
import React, { useState } from "react";
//Get Code Toggle
const [isActive, setActive] = useState("false");
const handleToggle = () => {
setActive(!isActive)
};
{isActive ? "Get Code" : "Hide Code"}
Required toggle div in loaded sub component LandingSub1.js
<div className={`codeview my-4 ${isActive ? "hide" : "show"}`}>
<pre>Code</pre>
</div>
https://codesandbox.io/s/call-function-from-main-component-to-inner-component-mp9mgp

pass the 'isActive' to your component:
<LandingSub1 isActive={isActive} />
deconstruct it in your child component:
const { isActive } = props;

Related

How do i get the Operations.js form data logged to the console , when the form in the parent component gets submitted?

So I have a Operations.js Component which gets rendered when a particular button in the parent component(ParamsForm.js) gets toggled. Now what I want is that when the form in the parent component gets submitted I want the data of the parent component form fields as well as the data of the child component form fields to get logged on the console . Is there any way to achieve this ???
ParamsForm.js
import React, { useState, useEffect } from 'react'
import { Form } from 'react-bootstrap'
import styles from '../style.module.css'
import Operations from './Operations'
const ParamsForm = () => {
const[isToggled,setIsToggled] = useState(false)
return (
<div className={styles.paramFormsContainer}>
<Form>
<button className={styles.paramFormsBtn}>http://localhost:3000/</button>
<input style={{flex : 1 }} type="text"></input>
<button type='button' onClick={()=>setIsToggled(!isToggled)} className={styles.pathParamFormsBtn}>Path Params</button>
{isToggled && <Operations></Operations>}
</Form>
</div>
)
}
export default ParamsForm
Operations.js
import React, { useEffect, useState } from 'react'
import styles from '../style.module.css'
import {FaInfo,FaFileInvoiceDollar} from 'react-icons/fa'
import ReactTooltip from "react-tooltip";
const Operations = () => {
const[isToggled,setIsToggled] = useState(true)
const[paramsList,setParamsList] = useState([{params: ""}])
useEffect(()=>{
console.log(paramsList)
console.log(isToggled)
},[isToggled])
const handleParamAdd = () =>{
setParamsList([...paramsList,{params:""}])
}
const handleParamRemove = (index) =>{
const list = [...paramsList]
list.splice(index,1)
setParamsList(list)
}
const handleParamsChange = (e,index)=>{
const{name,value} = e.target
const list = [...paramsList]
list[index][name] = value
setParamsList(list)
}
return (
<div >
<div className={styles.operationsBtnContainer}>
</div>
{isToggled && paramsList.map((singleParam,index)=>(<div key={index} className={styles.pathParamsFormParentContainer}>
<div className={styles.pathParamsFormChildContainer}>
<form>
<input name='name' value={singleParam.name} onChange={(e)=>handleParamsChange(e,index)} placeholder="Name..." style={{flex : 1 }} type="text"></input>
<select>
<option>any</option>
<option>string</option>
<option>number</option>
<option>integer</option>
<option>array</option>
</select>
<input placeholder="Description..." style={{flex : 1 }} type="text"></input>
{/* <button><FaFileInvoiceDollar></FaFileInvoiceDollar></button> */}
<button data-tip data-for="requiredTip"><FaInfo></FaInfo></button>
<ReactTooltip id="requiredTip" place="top" effect="float">
required
</ReactTooltip>
<button type='button' className={styles.addParamsBtn} onClick={handleParamAdd}><span>Add Parameter</span></button>
<button type='button' className={styles.removeParamsBtn} onClick={()=>handleParamRemove(index)}><span>Remove Parameter</span></button>
</form>
</div>
</div>)) }
</div>
)
}
export default Operations
There is no submit button in the parent component form, so you can't do anything when it's submitted.
Learn about putting answers of forms in state here
I would store every answer in its own state variable in the parent component, and pass the state set functions of the answers needed in the child to the child through props. You can then set the state through those functions in the child component and the parent component will have the state stored there already.
make a new piece of state for each answer
const [answers, setAnswer1] = useState("default")
pass the state to the child component via props
First, change the arguments of the child component's function to ({setAnswer1, setAnswer2, etc...})
Then pass the props to the child
<Operations setAnswer1={setAnswer1} setAnswer2={setAnswer2} etc.../>
handle input change, paste this inside the parent & child components
handleInputChange(event, setStateCallback) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
setStateCallback(value)
}
pass this function to each input, do the same in both parent and child components
<input onChange={(event) => handleInputChange(event, setAnswer1)}/>
after all this, you're ready to handle the submit event
Copy this function into the parent component
handleSubmit(event){
event.preventDefault()
console.log("answer 1's state: ", answer1)
console.log("answer 2's state: ", answer2)
// And so on for each piece of state
}
Above is not the cleanest solution, but it works. You could look into a for loop that takes an array of state variables and prints the value or something like that.
You also with need to add onSubmit={handleSubmit} to the form component in the parent component.

React: How to setState in child component from parent component?

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;

Triggering a function in a React parent functional component from its child

Seen similar issues here, but couldn't wrap my mind on how this works. New to functional components and React overall.
Parent contains the Child, which is a modal. Parent has a div that triggers showing the Child modal, and the Child modal has a close button that triggers its hiding. When I click on the div component in Parent, I need to show and hide the Child modal. When I click on the close button in the Child, I need to hide the Child component.
The Parent component:
import React, { useState } from "react";
import Child from "./Child";
const Parent = () => {
const [buttonState, setbuttonState] = useState({
buttonState: false,
});
const onParentClick = (e) => {
e.preventDefault();
setbuttonState(!buttonState);
};
return (
<div>
<div onClick={onParentClick}></div>
<Child isOpen={buttonState} onParentClick={onParentClick} />
</div>
);
};
export default Parent;
The Child component:
import React, { useState } from "react";
const Child = (props) => {
const [buttonState, setButtonState] = useState({
buttonState: props.isOpen,
});
const onChildClick = (e) => {
e.preventDefault();
setButtonState(false);
props.onParentClick();
};
return (
<div
className={
buttonState ? "child-modal-opened" : "child-modal-closed"
}
>
<div onClick={onChildClick}>Close</div>
</div>
);
};
export default Child;
For some reason, can't make this work. What am I missing here?
Looks like useState() is used incorrectly.
const [buttonState, setbuttonState] = useState({
buttonState: false,
});
results in buttonState being { buttonState: false}, so setbuttonState(!buttonState) does not work as intended.
Here's updated Parent component with useState(false) instead (setting initial buttonState value to false)
import React, { useState } from "react";
import Child from "./Child";
const Parent = () => {
const [buttonState, setbuttonState] = useState(false);
const onParentClick = (e) => {
e.preventDefault();
setbuttonState(!buttonState);
};
return (
<div>
<div onClick={onParentClick}></div>
<Child isOpen={buttonState} onParentClick={onParentClick} />
</div>
);
};
export default Parent;
P.S.
As #Will suggested, there is no need to create another state in Child, it can be passed from Parent
import React, { useState } from "react";
const Child = (props) => {
return (
<div
className={
props.isOpen ? "child-modal-opened" : "child-modal-closed"
}
>
<div onClick={props.onParentClick}>Close</div>
</div>
);
};
It looks like onParentClick is defined so as to take an event object as a parameter and call preventDefault() on that, but you're calling it without any arguments. Does it work like this: props.onParentClick(e);?

Trigger a function in another component on click a button from a different component in React

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

Changing a styled components style by toggling state after page load

I'm able to change a styled components styles based on state but only at page load, if I toggle the state after page load, the state changes successfully but the styles remain the same. The styled component whose styles I'm trying to change is "S.Search" which is inside of a "Search" component whose parent is "Navbar". I'm passing "Search" an "isVisible" state as a prop that is toggled by the onClick event handler in the parent component "Navbar".
The parent component:
import React, { useContext, useState } from "react"
import Search from "../components/Search"
import * as S from "../styled/Navbar"
const Navbar = () => {
const [isVisible, setVisibility] = useState(false)
return (
<S.Navbar>
/* S.SearchButton toggles the isVisible state onclick */
<S.SearchButton onClick={() => setVisibility(!isVisible)}>
<S.SearchButtonText>Search</S.SearchButtonText>
</S.SearchButton>
/* Passing isVisible state into the Search component as props */
<Search isVisible={isVisible} setVisibility={setVisibility} />
</S.Navbar>
)
}
export default Navbar
The search component:
import React, { useState } from "react"
import Icon from "../components/Icon"
import * as S from "../styled/Search"
const Search = props => {
return (
<S.Search>
<S.InputWrapper>
<S.SearchButtonIcon>
<Icon icon={"search"} />
</S.SearchButtonIcon>
<S.Input
type="text"
placeholder="Search for a character"
autocomplete="off"
/>
<S.ChampList></S.ChampList>
</S.InputWrapper>
</S.Search>
)
}
export default Search
I'm sure my state is being toggled correctly, but I have no idea why the style isn't being updated when toggling the state after page load. This is the styled component that has access to the isVisible state via props passed to the search component:
import styled from "styled-components"
export const Search = styled.div`
height: ${p => (!p.isVisible ? `0` : `2.5rem`)};
visibility: ${p => (!p.isVisible ? `hidden` : `visible`)};
opacity: ${p => (!p.isVisible ? `0` : `100`)};
display: flex;
justify-content: center;
transition: ${p => p.theme.easings.easeOut};
`
How can I change the style after page load when I toggle the state? Thank you for your time!
Your style isnt being applied because you are not passing isVisible prop to Search style-component.
You just need to do this in your Search Component:
const Search = props => {
return (
// HereĀ“s the trick
<S.Search isVisible={props.isVisible}>
.....
.....
.....

Categories