when I click the particular show more button the content should be displayed, the condition is the whole component should not re-rendering
I have used a useState when I clicked the button it is re-rendering the whole component
it is taking a long time to re-render every div
give an easy solution for this.
const [arr,setmarr] =useState([])
const oncl=(e)=>{
setarr((prev)=>[...prev,e.target.value])
}
return{
divarray.map((i,j)=>{
{console.log("tdic)")}
return(
<Commentbox divarr={arr[j]} value={j} oncl={(e)=>oncl(e) } />
)
}
}
Commentbox component
return
<div>
div{j}
// some icons here
{divarr && <div> right side div </div>}
<button onClick={(e)=>{oncl(e)}} value={j} >see more</button >
</div>
before onClick on showmore
after showmore button has been clicked on the second div
you should change each box to a component to solve this problem.
make that component with class base component because you need getSanpShotBeforeUpadte.
getSanpShotBeforeUpadte: you can control your component's render with this method.dont forget this method will give you nextProps,nextState and snapshot as parameter
class Box extends Component{
state = {
// more
showMore: false,
}
getSnapshotBeforeUpdate(nextProps,nextState){
// OTHER CONDITIONS
if(nextState.showMore !== this.state.showMore) return true
return false
}
render(){
return (
<div>
{/* CODE ... */}
<div style={{display: this.state.showMore ? 'block' : 'none'}}>
HERE IS A TEXT
</div>
<button onClick={()=>this.setState({showMore: !this.state.showMore})}>show more</button>
</div>
)
}
}
Related
I have a react app that I am working on, and currently, I have a custom-built dropdown that I want to open/close when a user clicks on the trigger(the arrow button), close it when a user selects an option, or close it when a user clicks outside the displayed component.
Here is my code:
For the sake of simplicity, I only added the code that I want help with.
class NavBar extends Component {
constructor(props) {
super(props);
this.state = {
showCurrencies: false,
};
this.handleShowCurrencies = this.handleShowCurrencies.bind(this);
}
componentDidMount() {
this.currencyRef = createRef();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.showCurrencies) return this.currencyRef.current.focus();
}
handleShowCurrencies = () => {
this.setState({
showCurrencies: !this.state.showCurrencies,
});
};
render() {
<div className="currency-switch" onClick={this.handleShowCurrencies}>
{currencySymbol}
<span>
<button>
<img src={`${process.env.PUBLIC_URL}/images/arrow.png`} />
</button>
</span>
</div>
{this.state.showCurrencies ? (
<div
className="dropdown"
tabIndex={"0"}
ref={this.currencyRef}
onBlur={this.handleShowCurrencies}
>
{currencies?.map((currency) => (
<div
key={currency.symbol}
className={`dropdown-items ${currencySymbol === currency.symbol ? "selected" : "" }`}
onClick={() => changeCurrencySymbol(currency.symbol)}
>
{`${currency.symbol} ${currency.label}`}
</div>
))}
</div>
) : null}
}
Currently, directing focus to a div element is working fine, and clicking outside the element as well. However, clicking back on the trigger or even selecting an option is not closing the div element. It seems like it is rendering twice(take a closer look on the console): https://drive.google.com/file/d/1ObxU__SbD_Upxr6qcy5eYO4LSy6Mzq9C/view?usp=sharing
Why is that happening? How can I solve it?
P.S: I don't often ask on StackOverflow, so am not familiar with the process. Please bear with me. If you need any other info, I will be more than happy to provide it.
Imagine I have a page "Parent" which conditionally renders a div "Child".
On the click of a button, "Child" opens. To close "Child" one has to click in a X button inside it.
This is how I would do it and in my opinion it looks clean.
const Parent = (props) => {
const [childVisible, setChildVisible] = useState(false);
return (
<>
{childVisible && <Child close={setChildVisible.bind(false)} />}
<button onClick={setChildVisible.bind(true)}>
Open Child
</button>
</>
)
}
const Child = (props) => {
return (
<div>
<p>Im Child</p>
<button onClick={props.close()}> X </button>
</div>
)
}
Since react v16.13.0 react has introduced a warning Warning: Cannot update a component from inside the function body of a different component. and it seems I can't do this anymore.
What's the correct pattern now? I would rather not have a state in both components stating the same thing.
Call back was not properly added .You could do like this onClick={props.close}
While use onClick={props.close()} like this. close() function run on child mount instead of click event
const Child = (props) => {
return (
<div>
<p>Im Child</p>
<button onClick={props.close}> X </button>
</div>
)
}
I am trying to show or hide a div in Reactjs using the state value in the CSS style option - display and I am using functions with hooks. I have a button and below the button a div. When i click the button i either want to hide or show the contents in the div based on whether it is currently shown or hidden.
This is the basic test code I have
import React, { useState } from "react";
function hide() {
return (
<div>
<Mycomp />
</div>
);
}
function Mycomp() {
const [dp, setDp] = useState("none");
return (
<form>
<button
onClick={() => {
setDp("block");
}}
>
Test
</button>
<div style={{ display: dp }}>Test</div>
</form>
);
}
export default hide;
I then use this hide component in my App.js file. When I click the button the new state is assigned but then the page re-renders and the initial state is loaded again almost immediately. How can I go by ensuring the new state is kept? Eventually I will create a function where if the div display or not based on the previous state.
The issue is that the button is inside a <form>. So any click on that button will submit the form and refresh the page.
Can I make a <button> not submit a form?
You need to add a type="button" to your <button>
import React, { useState } from "react";
function Hide() {
return (
<div>
<Mycomp />
</div>
);
}
function Mycomp() {
const [dp, setDp] = useState(false);
return (
<form>
<button
type="button"
onClick={() => setDp(!dp)}
>
Test
</button>
{dp && <div>Test</div>}
</form>
);
}
export default Hide;
Your code should be something like this, instead of using block and none as style we can use conditional JSX (which is more ideal approach) -:
function Mycomp(){
const[dp, toggleDp] = useState(false);
return(
<form>
<button onClick={()=>{toggleDp(!dp)}}>Test</button>
{dp && <div>Test</div>}
</form>
)
}
export default hide
A better implementation would be to have your state variable TRUE/FALSE value and based on it display the element using a conditional rendering, note e.preventDefault in the button handler to stop the refresh/redirect, here is a working snippet, also a codesandbox:
const { useState, useEffect } = React;
function App() {
return (
<div>
<Mycomp />
</div>
);
}
function Mycomp() {
const [dp, setDp] = useState(true);
return (
<form>
<button
onClick={(e) => {
e.preventDefault();
setDp(!dp);
}}
>
Test
</button>
{dp && <div>Test</div>}
</form>
);
}
ReactDOM.render(<App />, document.getElementById("react-root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>
I am starting my adventure with React so it is a hard time for me, however I prepared such pen for you to test. Here is a portion of code:
class App extends React.Component {
constructor() {
super();
this.state = {
settings: true,
next: false,
};
}
toggler(abc) {
console.log(">>", abc)
this.setState({
next: !this.state.next
/* {abc}: this.state.{abc} */
})
console.log(this.state.next)
}
render() {
return (
<div className="kalreg">
<MyButton name='settings' isActive={this.state.settings} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='settings2' isActive={this.state.settings} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='next' isActive={this.state.next} type="next" toggle={this.toggler.bind(this)}/>
</div>)
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
}
onChangeName(){
console.log(this.props.type)
if ( this.props.isActive ) { console.log("this one is active"); } else { console.log("ouch! it is not active, ignoring!"); return;}
this.props.toggle(this.props.type);
}
render () {
if ( this.props.isActive ) {
return ( <div className="button notVisible" onClick={this.onChangeName.bind(this)}>{this.props.name}</div>)
} else {
return ( <div className="button visible" onClick={this.onChangeName.bind(this)}>{this.props.name}</div>)
}
}
}
ReactDOM.render(<App />, document.getElementById("app"));
What I am trying to achieve is that when i press one of "settings" buttons (yellow) the "next" button becomes unclickable (green). There is a toggle function that every time I click settings button it turns on and off "next" button.
It works quite good, however it is just a draft of bigger project and i want to automate it a little bit.
As you can see I create my <MyButton> with both "isActive" and "type" props. But isActive holds what's inside this.state.settings while type is "settings". Instead of using two variables it would be great to pass only type of button to its component and component, depending on its type would check its parent's this.state.{type}. I used {type} because i would like to check it dynamically. Is that possible?
If so - how to do it?
My first attempt is to pass type from <MyButton> to <App> via toggler function. I named the variable "abc". I commented the way I wanted to do it because it doesn't work:
{abc}: !this.state.{abc}
Any idea to solve this problem would be more than appreciated.
Kalreg.
It is somewhat unclear what you are trying to achieve here. If you want to wire the state dynamically based on type, as you wrote in code: {abc}: !this.state.{abc} each button would toggle itself, not the next button. In this case your syntax is a little incorrect, it will work if you write it like:
[abc]: !this.state[abc]
However as I said, in your example, this makes the settings button change the state for this.state.settings disabling itself instead of the next button.
Another note would be, that if it is not necessary for the MyButton component to know its own type for other reasons, it is unnecessary to pass it as a prop and than make the component pass it back as an argument (this.props.toggle(this.props.type);). You can simply define the toggle function in the parent as:
toggle={() => this.toggler("settings")}
without passing type as a prop.
So basically we want to have the settings and settings2 buttons, and when we click on them, they toggle the state of the next button by making it un-clickable (green).
So if that is our goal, then
we don't need an isActive prop for the settings button. (Because it's always going to be active no matter what)
We also don't need to have a toggle prop on the Next button. (Because clicking the next button isn't supposed to toggle anything)
Instead of having two variables in the state why not just have one and then use that to determine the isActive prop of the next button?
The component would look like this:
constructor() {
super();
this.state = {
nextIsActive: false,
};
}
toggler() {
this.setState({
nextIsActive: !this.state.nextIsActive
})
console.log(this.state);
}
render() {
const {nextIsActive} = this.state
return (
<div className="kalreg">
<MyButton name='settings' isActive={true} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='settings2' isActive={true} type="settings" toggle={this.toggler.bind(this)}/>
<MyButton name='next' isActive={nextIsActive}/>
</div>
)
}
That way you don't have to have 2 state properties that you have to dynamically update because it adds more complexity to your application.
You can see the finished product here: Codepen
There are multiple divs on a page. What I want to do is get an attribute when a div is clicked. It is very easy in jquery but I am using Reactjs. Here is my code. It works but every time when I click on div attribute of the last element in is returned. Following is my code and the codepen URL.
https://codepen.io/anon/pen/gepVNP?editors=0010
class Content extends React.Component{
constructor(props) {
super(props)
this.click = this.click.bind(this)
}
click(){
// this.prop.setActiveMenu();
var summary = this.refs.summary;
console.log(summary.getAttribute('data-slug'))
}
render(){
return(
<div className="content">
{posts.map((post)=>{
return (
<div ref="summary" data-slug={post.slug} onClick={this.click} key={post.slug}>
<h1>{post.title}</h1>
<div>{post.content}</div>
<br/><br/><br/><br/><br/>
</div>
);
})}
</div>
);
}
}
That's because you're changing the ref element inside the map everytime you iterate on the posts array.
No need for refs here IMO.
Why not use the event.target from the click event?
onClick(event){
console.log(event.target.getAttribute('data-slug'))
}
BTW:
String refs are considered legacy. have a look here:
Why ref='string' is "legacy"?
I discourage you from using this approach, you could use a component instead.
The ref prop accepts a function and return the ref, so passing a string doesn't work.
class Content extends React.Component{
constructor(props) {
super(props)
this.click = this.click.bind(this)
}
click(){
console.log(this.summary.getAttribute('data-slug'))
}
render(){
return(
<div className="content">
{posts.map((post)=>{
return (
<div ref={ref => this.summary = ref} data-slug={post.slug} onClick={this.click} key={post.slug}>
<h1>{post.title}</h1>
<div>{post.content}</div>
<br/><br/><br/><br/><br/>
</div>
);
})}
</div>
);
}
}