In my code below, how to call executeBeforeToggle function before executing parent's function togglePage?
import * as React from "react";
import { useState } from "react";
import { render } from "react-dom";
const Page1 = (props) => {
//how to call this function before toggling?
const executeBeforeToggle = () => {
alert('HOORAY')
}
return (
<div>
<h1>I am Page 1</h1>
<button onClick={() => props.togglePage()}>Toggle</button>
</div>
)
};
const Page2 = (props) => {
return (
<div>
<h1>I am Page 2</h1>
<button onClick={() => props.togglePage()}>Toggle</button>
</div>
)
}
const App = () => {
const [page, setPage] = useState(false)
const togglePage = () => {
setPage(!page)
}
if(page === false) return <Page1 togglePage={togglePage} />
else return <Page2 togglePage={togglePage} />
}
render(<App />, document.getElementById("root"));
Not sure if I understood your question correctly, but actually that is simple:
const Page1 = (props) => {
const executeBeforeToggle = () => {
alert('HOORAY')
}
const handleClick = () => { // click handler which exec both funcs
executeBeforeToggle();
props.togglePage();
}
return (
<div>
<h1>I am Page 1</h1>
<button onClick={handleClick}>Toggle</button>
</div>
)
};
So the main idea is that your click handler should be a function which executes both executeBefore and togglePage functions in the order you need. You may write that right inside the buttons onClick, or (as above) create separate function and pass it into the onClick what makes code a bit more readable.
Related
I have found this error while trying to build another React app. So I am only asking the main issue here in a demo app, I might not be able to change any rendering methods here since it is not the actual project.
Issue in simplified form -> I was building a app where two count will be shown and a + button will be there next to that count value. When the button is clicked the count should be increased by 1. Unfortunately when I try to click on the button the value is increasing only the first time. After that the value is not even changing. But when I am implementing the same using Class component its working as expected.
Functional Component
import React, { useState } from "react";
function Page(props) {
const [count, setCount] = useState(0);
const [content, setContent] = useState({
button: (value) => {
return <button onClick={() => handlePlus(value)}>+</button>;
},
});
function handlePlus(value) {
console.log("value=", value);
const data = count + 1;
setCount((count) => data);
}
return (
<div>
<span>Functional Component Count = {count}</span>
{content.button(10)} // 10 will be replaced with another variable
</div>
);
}
export default Page;
Class Component
import React, { Component } from "react";
class PageClass extends Component {
state = {
count: 0,
content: {
button: (value) => {
return (
<button onClick={() => this.handlePlus(value)}>+</button>
);
},
},
};
handlePlus = (value) => {
console.log("value=", value);
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<span>Class Component Count = {this.state.count}</span>
{this.state.content.button(10)} // 10 will be replaced with another variable
</div>
);
}
}
export default PageClass;
App.js
import "./App.css";
import Page from "./components/Page";
import PageClass from "./components/PageClass";
function App() {
return (
<div className="App">
<Page />
<PageClass />
</div>
);
}
export default App;
However, If I replace that content state variable with normal const variable type and it is working as expected.
Below is working when I am not using any hooks to render the button.
But this is not helpful for my case.
const content = {
content: () => {
console.log(count);
return <button onClick={() => handlePlus(value)}>+</button>;
},
};
I was trying to create some re-usable components and hence I wanted to have that function in state variable which return button tag, so that I can implements some other logic there.
The value will be missing since you're passing a hard-coded 10.
I'd recommend simplifying the handlePlus to just:
setCount(c => c + 1);
Then set the onclick like so:
<button onClick={handlePlus}>+</button>
And your code will work as expected as you can see in this snippet:
const { useState } = React;
const Example = () => {
const [count, setCount] = useState(0);
const [content, setContent] = useState({
content: (value) => {
return <button onClick={handlePlus}>+</button>;
},
});
function handlePlus(value) {
setCount(c => c + 1);
}
return (
<div>
<span>{count}</span>
{content.content(10)}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
That said, I'd recommend removing the button from the hook, and just render it yourself:
const { useState } = React;
const Example = () => {
const [count, setCount] = useState(0);
function handlePlus(value) {
setCount(c => c + 1);
}
return (
<div>
<span>{count}</span>
<button onClick={handlePlus}>+</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
See React documentation about the c => c + 1 syntax
I'm having an issue with react memo when using nextjs. In the _app e.g. I have a button imported:
import { ChildComponent } from './ChildComponent';
export const Button = ({ classN }: { classN?: string }) => {
const [counter, setCounter] = useState(1);
const Parent = () => {
<button onClick={() => setCounter(counter + 1)}>Click me</button>
}
return (
<div>
{counter}
<Parent />
<ChildComponent />
</div>
);
};
Child component:
import React from 'react';
export const ChildComponent = React.memo(
() => {
React.useEffect(() => {
console.log('rerender child component');
}, []);
return <p>Prevent rerender</p>;
},
() => false
);
I made one working in React couldn't figure it out in my own app:
https://codesandbox.io/s/objective-goldwasser-83vb4?file=/src/ChildComponent.js
The second argument of React.memo() must be a function that returns true if the component don't need to be rerendered and false otherwise - or in the original definition, if the old props and the new props are equal or not.
So, in your code, the solution should be just change the second argument to:
export const ChildComponent = React.memo(
() => { ... },
// this
() => true
);
Which is gonna tell React that "the props didn't change and thus don't need to rerender this component".
So my issue was that I made a function called Button and returned inside a button or Link. So I had a mouseEnter inside the button which would update the state and handle the function outside the function. Kinda embarrassing. This fixed it. So the only change was I moved usestate and handlemousehover inside the button function.
const Button = () => {
const [hover, setHover] = useState(false);
const handleMouseHover = (e: React.MouseEvent<HTMLElement>) => {
if (e.type === 'mouseenter') {
setHover(true);
} else if (e.type === 'mouseleave') setHover(false);
};
return (
<StyledPrimaryButton
onMouseEnter={(e) => handleMouseHover(e)}
onMouseLeave={(e) => handleMouseHover(e)}
>
<StyledTypography
tag="span"
size="typo-20"
>
{title}
</StyledTypography>
<ChildComponent />
</StyledPrimaryButton>
);
};
I need to show the props value (which is a simple string). Each time I get new search results, I'm sending in the props. At the very first render the props will always be undefined.
Edit:
Header.jsx
function Header() {
const [searchString, setString] = useState('');
const onChangHandler = (e) => {
setString(e.target.value);
};
const activeSearch = () => {
if (searchString.length > 0) {
<Home searchResults={searchString} />;
}
};
return (
<div>
<input
placeholder='Search here'
value={searchString}
onChange={(e) => onChangHandler(e)}
/>
<button onClick={activeSearch}>Search</button>
</header>
</div>
);
}
I searched for previous stackoverflow questions and reactjs.org but found no answer.
Home.jsx
import React, { useEffect, useState } from 'react';
function Home({ searchResults }) {
const [itemSearchResults, setResults] = useState([]);
const [previousValue, setPreviousValue] = useState();
// What function will re-render when the props are first defined or changed ?
useEffect(() => { // Doesn't work
setResults(searchResults);
}, [searchResults]);
return (
<div>
<h3>Home</h3>
<h1>{itemSearchResults}</h1>
</div>
);
}
export default Home;
App.js
function App() {
return (
<div className='App'>
<Header />
<Home />
<Footer />
</div>
);
}
I'm sending the input string only to check if the props will change at the child component ("Home").
Any experts here know what's the problem?
Why it doesn't work?
It's because the Home component is never used, even if it's included in the following snippet:
const activeSearch = () => {
if (searchString.length > 0) {
<Home searchResults={searchString} />;
}
};
The activeSearch function has a couple problems:
it is used as an event handler though it uses JSX (outside the render phase)
it doesn't return the JSX (would still fail inside the render phase)
JSX should only be used within the render phase of React's lifecycle. Any event handler exists outside this phase, so any JSX it might use won't end up in the final tree.
The data dictates what to render
That said, the solution is to use the state in order to know what to render during the render phase.
function Header() {
const [searchString, setString] = useState('');
const [showResults, setShowResults] = useState(false);
const onChangHandler = (e) => {
// to avoid fetching results for every character change.
setShowResults(false);
setString(e.target.value);
};
const activeSearch = () => setShowResults(searchString.length > 0);
return (
<div>
<input
value={searchString}
onChange={(e) => onChangHandler(e)}
/>
<button onClick={activeSearch}>Search</button>
{showResults && <Home query={searchString} />}
</div>
);
}
useEffect to trigger effects based on changing props
And then, the Home component can trigger a new search request to some service through useEffect.
function Home({ query }) {
const [results, setResults] = useState(null);
useEffect(() => {
let discardResult = false;
fetchResults(query).then((response) => !discardResult && setResults(response));
// This returned function will run before the query changes and on unmount.
return () => {
// Prevents a race-condition where the results from a previous slow
// request could override the loading state or the latest results from
// a faster request.
discardResult = true;
// Reset the results state whenever the query changes.
setResults(null);
}
}, [query]);
return results ? (
<ul>{results.map((result) => <li>{result}</li>))}</ul>
) : `Loading...`;
}
It's true that it's not optimal to sync some state with props through useEffect like the article highlights:
useEffect(() => {
setInternalState(externalState);
}, [externalState]);
...but in our case, we're not syncing state, we're literally triggering an effect (fetching results), the very reason why useEffect even exists.
const { useState, useEffect } = React;
const FAKE_DELAY = 5; // seconds
function Home({ query }) {
const [results, setResults] = useState(null);
useEffect(() => {
let queryChanged = false;
console.log('Fetch search results for', query);
setTimeout(() => {
if (queryChanged) {
console.log('Query changed since last fetch, results discarded for', query);
return;
}
setResults(['example', 'result', 'for', query])
}, FAKE_DELAY * 1000);
return () => {
// Prevent race-condition
queryChanged = true;
setResults(null);
};
}, [query]);
return (
<div>
{results ? (
<ul>
{results.map((result) => (
<li>{result}</li>
))}
</ul>
) : `Loading... (${FAKE_DELAY} seconds)`}
</div>
);
}
function Header() {
const [searchString, setString] = useState('');
const [showResults, setShowResults] = useState(false);
const onChangHandler = (e) => {
// to avoid fetching results for every character change.
setShowResults(false);
setString(e.target.value);
};
const activeSearch = () => setShowResults(searchString.length > 0);
return (
<div>
<input
placeholder='Search here'
value={searchString}
onChange={(e) => onChangHandler(e)}
/>
<button onClick={activeSearch}>Search</button>
{showResults && <Home query={searchString} />}
</div>
);
}
ReactDOM.render(<Header />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Better solution: Uncontrolled inputs
Another technique in your case would be to use an uncontrolled <input> by using a ref and only updating the search string on click of the button instead of on change of the input value.
function Header() {
const [searchString, setString] = useState('');
const inputRef = useRef();
const activeSearch = () => {
setString(inputRef.current.value);
}
return (
<div>
<input ref={inputRef} />
<button onClick={activeSearch}>Search</button>
{searchString.length > 0 && <Home query={searchString} />}
</div>
);
}
const { useState, useEffect, useRef } = React;
const FAKE_DELAY = 5; // seconds
function Home({ query }) {
const [results, setResults] = useState(null);
useEffect(() => {
let queryChanged = false;
console.log('Fetch search results for', query);
setTimeout(() => {
if (queryChanged) {
console.log('Query changed since last fetch, results discarded for', query);
return;
}
setResults(['example', 'result', 'for', query])
}, FAKE_DELAY * 1000);
return () => {
// Prevent race-condition
queryChanged = true;
setResults(null);
};
}, [query]);
return (
<div>
{results ? (
<ul>
{results.map((result) => (
<li>{result}</li>
))}
</ul>
) : `Loading... (${FAKE_DELAY} seconds)`}
</div>
);
}
function Header() {
const [searchString, setString] = useState('');
const inputRef = useRef();
const activeSearch = () => {
setString(inputRef.current.value);
}
return (
<div>
<input
placeholder='Search here'
ref={inputRef}
/>
<button onClick={activeSearch}>Search</button>
{searchString.length > 0 && <Home query={searchString} />}
</div>
);
}
ReactDOM.render(<Header />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Passing the state around
[The following line] brings the Home component inside the Header component, which makes duplicate
{searchString.length > 0 && <Home query={searchString} />}
In order to make the Header component reusable, the quickest way would be to lift the state up.
// No state needed in this component, we now receive
// a callback function instead.
function Header({ onSubmit }) {
const inputRef = useRef();
const activeSearch = () => {
// Uses the callback function instead of a state setter.
onSubmit(inputRef.current.value);
}
return (
<div>
<input ref={inputRef} />
<button onClick={activeSearch}>Search</button>
</div>
);
}
function App() {
// State lifted up to the parent (App) component.
const [searchString, setString] = useState('');
return (
<div className='App'>
<Header onSubmit={setString} />
{searchString.length > 0 && <Home query={searchString} />}
<Footer />
</div>
);
}
If that solution is still too limited, there are other ways to pass data around which would be off-topic to bring them all up in this answer, so I'll link some more information instead:
Thinking in React
What's the right way to pass form element state to sibling/parent elements?
Passing data to sibling components with react hooks?
Application State Management with React
How can I update the parent's state in React?
Top 5 React state management libraries in late 2020 (Redux, Mobx, Recoil, Akita, Hookstate)
if your props are passed as searchResults, then change the props to,
function Home({ searchResults}) {...}
and use
useEffect(() => { // code, function },[searchResults]) ).
I am trying to create dynamically element on button click and append it to one of the classes by using ref.
I can use document.createElement but from what I read do not use it in react
For example I want to add an element of <p> to div with class name of classes.elements by clicking the button
import React, { useRef } from 'react'
import classes from './AddElement.scss'
const AddElement = (props) => {
const elementRef = useRef(null)
const addElement = () => {
<p>This is paragraph</p>
}
return (
<div>
<button onClick={() => addElement()}>Click here</button>
<div ref={elementRef} className={classes.elements}>
</div>
</div>
)
}
export default AddElement;
You could try using the useState hook like this :
import React, { useState } from 'react';
import classes from './AddElement.scss';
const AddElement = () => {
const [dynamicElems, setDynamicElems] = useState([]);
const addElement = () => {
// Creates the dynamic paragraph
const newDynamicElem = <p className={classes.elements}>This is paragraph</p>;
// adds it to the state
setDynamicElems(() => [...dynamicElems, newDynamicElem]);
};
return (
<div>
<button onClick={() => addElement()}>Click here</button>
<div className={classes.elements}>{dynamicElems}</div>
</div>
);
};
export default AddElement;
const AddElement = (props) => {
const [dynamicCompList, setDynamicCompList] = useState([]);
const addElement = () => {
const dynamicEl = React.createElement("p", {}, "This is paragraph");
setDynamicCompList(dynamicCompList.concat(dynamicEl));
}
return (
<div>
<button onClick={() => addElement()}>Click here</button>
<div className={classes.elements}>
{dynamicCompList}
</div>
</div>
)
}
export default AddElement;
Try this:
const addElement = () => {
const para = document.createElement("p");
para.innerHTML = 'Hello';
elementRef.current.appendChild(para);
};
<div ref={elementRef}></div>
<button onClick={addElement}>Click me</button>
Approach 1
import React, { useRef, useState } from "react";
import classes from "./App.module.scss";
export default function App() {
const componetRef = useRef(null);
const [contentValue, setContentValue] = useState([]);
const addElement = () => {
const content = "this is para";
const type = componetRef.current.dataset.type || "p";
const classNames = componetRef.current.className;
const elemnt = React.createElement(type, { key: Date.now() }, content);
setContentValue([
...contentValue, // If you dont want to make it multiple times. just remove it
elemnt
]);
};
return (
<div className="App">
<button onClick={addElement}>Click here</button>
<div data-type="h1" ref={componetRef} className={classes.tag1}>
{contentValue}
</div>
</div>
);
}
Approach 2
import React, { useState } from "react";
import classes from "./App.module.scss";
const NewComponent = ({ classNames, content }) => {
return (
<div className={classNames} dangerouslySetInnerHTML={{ __html: content }} />
);
};
export default function App() {
// const classRef = useRef(null);
const [multiple, setMultiple] = useState([]);
const addElement = (e) => {
const classNames = e.target.dataset.class;
const content = e.target.dataset.content;
setMultiple([
...multiple, // If you dont want to make it multiple times. just remove it
<NewComponent
key={Date.now()}
classNames={classNames}
content={content}
/>
]);
};
return (
<div className="App">
<button
onClick={addElement}
data-class={classes.tag1}
data-content={"<p>asdasd</p>"}
>
Click here
</button>
{multiple}
</div>
);
}
Let me know if you have more question.
Here is sandbox
I have following components .
this component accept props which is function that can be called.
deleteIcon = () => {
console.log("deleting the document");
}
export const Parent = (props) => {
return (
<button onclick={() => {deleteIcon()}}
)
}
Now, I have some another component which uses this component. which has its own implementation of the deleteIcon method.
deletechildIcon = () => {
}
export const child = () => {
return (
<Parent deleteIcon={() => {deletechildIcon()}} />
)
}
So, from child still it is calling the parent method and not the child one. can any one help me with this ?
Some notice points:
onClick rather than onclick
no need to use arrow function inside props, which may cause performance loss and it's not the best practice
write your functions inside the component
Child component is the one which been called inside the Parent, you made it the opposite
Try the demo in-text:
const Parent = () => {
const deleteIcon = () => {
console.log("deleting the document");
};
return <Child deleteIcon={deleteIcon} />;
};
const Child = props => {
return <button onClick={props.deleteIcon}>XXX</button>;
};
ReactDOM.render(<Parent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
You are calling the deleteIcon method in your Parent component, not the props.deleteIcon
It sounds like you want Parent to have a default deleteIcon prop that can be optionally overridden in specific implementations. You could do that by editing Parent like so:
deleteIcon = () => {
console.log("deleting the document");
}
export const Parent = (props) => {
const buttonOnClickHandler = props.deleteIcon || deleteIcon;
return (
<button onClick={() => {buttonOnClickHandler()}}
)
}
Or you could use default arguments:
deleteIconDefault = () => {
console.log("deleting the document");
}
export const Parent = ({
deleteIcon = deleteIconDefault
}) => {;
return (
<button onClick={() => {this.props.deleteIcon()}}
)
}
Hope this points you in the right direction!