I'm using react and I have 2 components. Inside Comp1 I have a function that I want to call inside Comp2. How could I do this?
Code:
const Comp1 = () => {
// Call handleSetSearch in Comp2
const handleSetSearch = () => {
const value = document.querySelector('.ais-SearchBox-input').value;
console.log(value)
};
return (
<>
../
</>
);
}
const Comp2 = (handleSetSearch) => {
return (
<div>
<p onClick={ComparisonSearch}></p>
</div>
)
};
In react, using props to communicate with components is very common. For example, if the relation between Comp 1 and Comp 2 is parent to child, you can acquire the function of handleSetSearch by using props.
Const Comp1 = () => {
const handleSetSearch = () => {
const value = document.querySelector('.ais-SearchBox-input').value;
console.log(value)
};
return (
<>
<Comp2 handleSetSearch={handleSetSearch} />
</>
)
}
Const Comp2 = (props) => {
return (
<div>
<p onClick={props.handleSetSearch}></p>
</div>
)
}
If you want to add parameter in the function of handleSetSearch. Here is a example.
const handleSetSearch = (params) => {
console.log(params)
};
<p onClick={() => props.handleSetSearch('123')}></p>
You should use Comp2 as a child in Comp1 and pass Comp1 function into Comp2 as props.
const Comp1 = () => {
const handleSetSearch = () => {};
return (
<>
<Comp2 handleSetSearch={handleSetSearch} />
</>
);
}
const Comp2 = ({handleSetSearch}) => {
return (
<div>
<p onClick={() => handleSetSearch()}></p>
</div>
)
};
Related
I have a useRef hook and two components. In one component, I increase the value on click by 1 unit, and in the second component, I draw the value. I pass the value itself through useContext.
Now the problem is that the value is not being redrawn. How can this be fixed?
export const ContactContext = React.createContext();
function App() {
const countItem = useRef(1);
const value = { countItem };
return (
<ContactContext.Provider value={value}>
<div>
<AddValue />
</div>
<div>
<Logo />
</div>
</ContactContext.Provider>
);
}
const AddValue = () => {
const { countItem } = useContext(ContactContext);
const addItemHandler = () => {
countItem.current = countItem.current + 1;
};
return (
<>
<div>
<button
onClick={addItemHandler}
>
<img src="plus.svg" alt="plus logo" />
</button>
</div>
</>
);
};
function Logo() {
const { countItem } = useContext(ContactContext);
return (
<p data-testid="statistics">
{`Count of channels: ${countItem.current}`} <br />
</p>
);
}
useRef wont cause components in React to rerender
function App() {
const [countItem, setCountItem] = useState(1)
const value = { countItem, setCountItem };
In AddValue
const AddValue = () => {
const { countItem, setCountItem } = useContext(ContactContext);
const addItemHandler = () => {
setCountItem(c => c +1)
};
Reading the new React docs for state management will help
Hope it helps
Replace useRef with useState.
useRef update the value but does not rerender.
I am making a calculator using react.
Every time I press a number button, the whole application re-renders, instead of the <Display />.
To prevent it, I tried 2 different approaches for App, But neither of them worked.
Here is the sandbox link.
Any help would be appreciated.
Put clickHandler inside of useCallback()
const App = () => {
const [screen, setScreen] = useState("0");
console.log("render");
const clickHandler = useCallback(
(val) => {
if (val === "AC") {
setScreen("");
return;
}
screen === "0" ? setScreen(val) : setScreen(screen + val);
},
[screen]
);
return (
<div className="App">
<div className="display">{screen}</div>
<ButtonList clickHandler={clickHandler} />
</div>
);
};
Put Display component inside of React.memo
const App = () => {
const [screen, setScreen] = useState("0");
console.log("render");
const clickHandler = (val) => {
if (val === "AC") {
setScreen("");
return;
}
screen === "0" ? setScreen(val) : setScreen(screen + val);
};
const displayComponent = () => {
return (
<>
<div className="display">{screen}</div>
<ButtonList clickHandler={clickHandler} />
</>
);
};
const MemoizedComponent = React.memo(displayComponent);
return (
<div className="App">
<MemoizedComponent />
</div>
);
};
And here's the ButtonList & Button component.
export const ButtonList = ({ clickHandler }) => {
const arr = [...Array.from(Array(10).keys()).reverse(), "AC"];
return (
<div className="buttons">
<div className="numbersWrapper">
{arr.map((item) => (
<Button
key={item}
clickHandler={clickHandler}
value={item.toString()}
/>
))}
</div>
</div>
);
};
export const Button = ({ value, clickHandler }) => {
return (
<button
name={value}
onClick={() => {
clickHandler(value); //where the clickEvent happens
}}
>
{value}
</button>
);
};
If you don't want a component re-render,You would have to define the click handler in another component that you would like to re-render.
So do it like this:
const App = () => {
console.log("render");
return (
<div className="App">
<childComponent />
</div>
);
};
export const childComponent = () => {
const [screen, setScreen] = useState("0");
const clickHandler = (val) => {
if (val === "AC") {
setScreen("");
return;
}
screen === "0" ? setScreen(val) : setScreen(screen + val);
};
return (
<>
<div className="display">{screen}</div>
<ButtonList clickHandler={clickHandler} />
</>
);
}
This way you prevent a particular component from re-rendering. But note that if you update a state or do anything from which causes re-renders from the parent component, It would equally re-render the child component.
I have an array of fragments that I'm passing into ChildComponent. The fragments must have onChange attributes set to a handler that exists inside the ChildComponent, but as they are written outside of it doing so breaks the app. I can't define the fragments inside ChildComponent and I can't define ChildComponent inside ParentComponent. How do I do this properly?
const fragments = [
const Screen1 = () => {
return (
<>
<input type="text" id="screen1_input1" onChange={onChangeHandler} />
</>
)
};
const Screen2 = () => {
return (
<>
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
</>
)
};
]
ChildComponent.js
const ChildComponent = (props) => {
let index = props.index
const fragments = props.fragments
const onChange = (e) => {
//whatever
}
return (
<>
<h2 className="screens">
{fragments[index]()}
</h2>
</>
)
}
ParentComponent.js
import ChildComponent from './ChildComponent'
const ParentComponent = (props) => {
let index = 3
return (
<ChildComponent index='3'/>
)
}
You can convert fragments array into a function which takes onChangHandler and return an array itself.
Below is the refactored code, for simplicity I'm just logging the input element id and the value that's being inputted.
const { Fragment } = React;
const fragments = (onChangeHandler) =>
[
<input type="text" id="screen1_input1" onChange={onChangeHandler} />,
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
];
const ChildComponent = ({ index, fragments }) => {
const onChange = e => {
const { target: {id, value} } = e;
console.log(id, value);
};
return (
<Fragment>
<h2 className="screens">{fragments(onChange)[index]}</h2>
</Fragment>
);
};
const ParentComponent = props => {
return <ChildComponent index={1} fragments={fragments}/>;
};
ReactDOM.render(<ParentComponent />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Define fragments like this
const fragments = [
const Screen1 = (onChangeHandler) => {
return (
<>
<input type="text" id="screen1_input1" onChange={onChangeHandler} />
</>
)
};
const Screen2 = (onChangeHandler) => {
return (
<>
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
</>
)
};
]
ChildComponent.js
const ChildComponent = (props) => {
let index = props.index
const fragments = props.fragments
const onChange = (e) => {
//whatever
}
return (
<>
<h2 className="screens">
{fragments[index](onChange)}
</h2>
</>
)
}
Using Reac.memo to wrap my functional component, and it can run smoothly, but the eslint always reminded me two errors:
error Component definition is missing display name react/display-name
error 'time' is missing in props validation react/prop-types
Here is my code:
type Data = {
time: number;
};
const Child: React.FC<Data> = React.memo(({ time }) => {
console.log('child render...');
const newTime: string = useMemo(() => {
return changeTime(time);
}, [time]);
return (
<>
<p>Time is {newTime}</p>
{/* <p>Random is: {children}</p> */}
</>
);
});
My whole code:
import React, { useState, useMemo } from 'react';
const Father = () => {
const [time, setTime] = useState(0);
const [random, setRandom] = useState(0);
return (
<>
<button type="button" onClick={() => setTime(new Date().getTime())}>
getCurrTime
</button>
<button type="button" onClick={() => setRandom(Math.random())}>
getCurrRandom
</button>
<Child time={time} />
</>
);
};
function changeTime(time: number): string {
console.log('changeTime excuted...');
return new Date(time).toISOString();
}
type Data = {
time: number;
};
const Child: React.FC<Data> = React.memo(({ time }) => {
console.log('child render...');
const newTime: string = useMemo(() => {
return changeTime(time);
}, [time]);
return (
<>
<p>Time is {newTime}</p>
{/* <p>Random is: {children}</p> */}
</>
);
});
export default Father;
It's because you have eslint config which requries you to add displayName and propTypes
Do something like
const Child: React.FC<Data> = React.memo(({ time }) => {
console.log('child render...');
const newTime: string = useMemo(() => {
return changeTime(time);
}, [time]);
return (
<>
<p>Time is {newTime}</p>
{/* <p>Random is: {children}</p> */}
</>
);
});
Child.propTypes = {
time: PropTypes.isRequired
}
Child.displayName = 'Child';
If you are working with React and TypeScript, you can turn off the react/prop-types rule.
This is because TypeScript interfaces/props are good enough to replace React's prop types.
Parent Component:
const initialValue_modalProps = [
{ show: false, response: "" }
];
const [modalProps, setModalProps] = useState(initialValue_modalProps)
const passedFunction = () => {
setModalProps(modalProps => initialValue_modalProps);
}
..
..
<div>
<Modal show={modalProps.show}
response={modalProps.response}
passedFunction={passedFunction}></Modal>
</div>
Child Component:
export default function ModalComp(props) {
const [modalOpen, setmodalOpen] = useState(true);
console.log('modalOpen', modalOpen);
if (props.show === false || modalOpen === false) {
return null;
}
return (<Modal isOpen={props.show}>
<ModalHeader>Deployment Status</ModalHeader>
<ModalBody>{props.response}</ModalBody>
<ModalFooter>
<Button onClick={() => {
setmodalOpen(modalOpen => false);
props.passedFunction();
}}>Close</Button>
</ModalFooter>
</Modal>)
}
Here I want to passedFunction function from Parent to child so that the Child component can execute it to reset the state in parent
You can take this as an reference with live example demo https://codesandbox.io/s/modal-6fvyx
function App() {
const [status, setState] = React.useState(false);
const [text, setText] = React.useState("");
const handleClick = () => {
setState(prevStatus => !prevStatus);
};
const handleChange = e => {
setText(e.target.value);
};
return (
<>
<button onClick={handleClick}>Open photo entry dialog</button>
<ChildComponent
isOpen={status}
text={text}
handleChange={handleChange}
handleClick={handleClick}
/>
</>
);
}
const ChildComponent = ({ isOpen, text, handleChange, handleClick }) => {
return (
<>
{isOpen && (
<Model
status={isOpen}
handleClick={handleClick}
text={text}
handleChange={handleChange}
/>
)}
</>
);
};
You need to remove the parentheses behind passedFunction, because otherwise you are executing the function first and passing the result to the child afterwards. Pass your function as it is via passedFunction={passedFunction}.
const ParentComponent = () => {
const initialModalProps = { ... };
const [modalProps, setModalProps] = useState(initialModalProps);
const passedFunction = () => {
setModalProps(initialModalProps);
}
return (
<div>
<Modal
show={modalProps.show}
response={modalProps.response}
passedFunction={passedFunction} />
</div>
);
};
Changed the child component to this. and its working
export default function ModalComp(props) {
//const [modalOpen, setmodalOpen] = useState(true);
if (props.show === false) {
return null;
}
return (<Modal isOpen={props.show}>
<ModalHeader>Deployment Status</ModalHeader>
<ModalBody>{props.response}</ModalBody>
<ModalFooter>
<Button onClick={props.passedFunction}>Close</Button>
</ModalFooter>
</Modal>)