i want to parse data from child to parent i have try solution from question
How to parse data from child to parent using reactjs?
I print that state and what appears is the state of the previous action, not the state of the last action
I tried to implement this to bring up content based on the menu that was clicked
example:
i have 3 menu
- A
- B
- C
when i click first time at the menu, for example A. the state in console is '', Then Second time i click B, the state in console is A
this is my code
PARENT
changeMenu= (menu) =>{
this.setState({
menu: menu
});
console.log('menu',menu); // Show State
}
render(){
return (
<LeftMenuMycommission active="0" menu = {(value) => this.changeMenu(value)}/>
CHILD
menuClick = (menu_name, active) =>{
this.setState({
menu: menu_name,
})
this.props.menu(this.state.menu);
}
render (){
render (
<ul>
<li ><a onClick={this.menuClick.bind(this, "A")}><i className={"fa fa-circle"}></i> A</a></li>
<li ><a onClick={this.menuClick.bind(this, "B")}><i className={"fa fa-circle"}></i> B</a></li>
<li ><a onClick={this.menuClick.bind(this, "C")}><i className={"fa fa-circle"}></i> C</a></li>
</ul>
Can anyone help me to find the problem?
Any help would be appreciated thank you :)
It's not guaranteed that state is updated immediately. You would need to use callback function and then call your parent method to pass the child component state to the parent correctly:
menuClick = (menu_name, active) =>{
this.setState({
menu: menu_name,
}, () => {
this.props.menu(this.state.menu);
})
}
Or, componentDidUpdate will do the same job:
componentDidUpdate() {
this.props.menu(this.state.menu) // only called after component is updated
}
Related
https://codesandbox.io/s/little-thunder-so1omh?file=/src/menu/menu.scss
this is my problem. when I refresh, menu opens and closes for a moment
I want to prevent this from re-render.
this my console.log when i refresh every time:
false 'open'
menu.jsx:23 item
menu.jsx:25 rerendered
menu.jsx:22 true 'open'
menu.jsx:23 undefined 'item'
menu.jsx:25 rerendered
I went through the code, its not the render that is causing the it to open and close, its the .collapse class animation
you can verify the case by using a ref
// style modification
.hidden {
visibility: hidden !important
}
// this is a flag to detect atleast one click
// on the menu item
// this will be false when app loads the first time
// then after user click on the menu, it will set to true
let isSelectedOnce = React.useRef(false);
const handleDropDown = (id) => {
setItemPressed(id);
if (itemPressed !== id) {
setOpen(true);
} else {
setOpen((pre) => !pre);
}
if (!isSelectedOnce.current) {
isSelectedOnce.current = true;
}
};
return (
...
<ul
className={`collapse ${
open && itemPressed === "menu" ? "show" : ""
} ${!isSelectedOnce.current ? "hidden" : ""}`}
>
<li>Menu Category</li>
<li>products list</li>
<li>Add product</li>
</ul>
</li>
I think you need to keep the menu items collapsed to start with
Hope this helps you in finding a better solution
Edit: more about when to use refs
this is not a bug but a feature, react after 16.3.0. will render twice as for development mode. see the issue on react repo below, happy coding, you doing great!
https://github.com/facebook/react/issues/15074
There was an issue in SCSS. menu opens and closes is fixed now you can check
https://codesandbox.io/s/eager-microservice-b7p4gc?file=/src/menu/menu.scss
I have a Component that renders all the children passed to it as list elements. For example:
// more or less the logic of my component
const ListifyChildren = (props) => {
return <ul>{props.children.map(element => <li>{element}</li>)}</ul>
}
// example use
<ListifyChildren>
<div>Some component</div>
<div>Some other component</div>
</ListifyChildren>
Would produce
<ul class="listify">
<li class="listify-list-item">Some component</li>
<li class="listify-list-item">Some other component</li>
</ul>
But the problem is I want to be able to use HoC's that return a list of components and to treat those components as children of my actual list. For example:
const ReturnSomeStuff = () => {
return [someArray].map(element => <div>{element}</div>
}
<ListifyChildren>
<ReturnSomeStuff/>
</ListifyChildren>
//what I get:
<ul class="listify">
<li class="listify-list-item">
<div>something</div>
<div>something</div>
<div>something</div>
</li>
</ul>
//what I want to get:
<ul class="listify">
<li class="listify-list-item">something</li>
<li class="listify-list-item">something</li>
<li class="listify-list-item">something</li>
</ul>
How can I make sure that my component maps over actual html children, not the function calls passed to it?
You can use Fragment to do it: https://reactjs.org/docs/fragments.html
const ReturnSomeStuff = () => {
return [someArray].map(element => <>{element}</>
}
I have found the answer: the key is referring to child elements via React.Children API rather than simple props.children - it returns the list of rendered elements.
I'm working on creating a multi level menu and I have sucessfully created a toggle which opens the sub menu on click. The issue I am having however is on click, all of the sub menus are opening. Here is my code so far:
Function
const [isSubOpen, setIsSubOpen] = useState(false)
const toggleSubMenu = (index, e) => {
e.preventDefault()
console.log(index.key)
let test = e.currentTarget.nextElementSibling.id
console.log(test)
if (test == index.key) {
setIsSubOpen(!isSubOpen)
}
}
Menu
<ul>
<li>
<a href={item.url}
onClick={toggleSubMenu.bind(this, { key })}
>
</a>
</li>
</ul>
Sub menu
<div id={key} css={isSubOpen ? tw`block` : tw`hidden`}></div>
Using a single boolean for all of them will cause them all to open and close whenever the state changes. If you want to keep it all in one state, you can use an array or an object to manage each sub-menu. An array would be easiest, so I'll show an example of how that would work.
Your state would be an array consisting of booleans. Each index would represent a sub-menu, false would be closed and true would be open. So if you click to open the first sub-menu at index 0, you would set the array to [true, false].
// Initialize the state with `false` for each sub-menu
const [subMenuState, setSubMenuState] = useState([false, false])
const toggleSubMenu = (e, i) => {
e.preventDefault()
// Clone the array
const newState = subMenuState.slice(0)
// Toggle the state of the clicked sub-menu
newState[i] = !newState[i]
// Set the new state
setSubMenuState(newState)
}
Whenever you call toggleSubMenu, you would pass the index as the second parameter like so:
<ul>
<li>
<a href="#" onClick={e => toggleSubMenu(e, 0)}>
Link 1
</a>
</li>
<li>
<a href="#" onClick={e => toggleSubMenu(e, 1)}>
Link 2
</a>
</li>
</ul>
Then reference that index in the sub-menu to see whether or not it's open:
<div css={subMenuState[0] ? tw`block` : tw`hidden`}>Sub-menu 1</div>
<div css={subMenuState[1] ? tw`block` : tw`hidden`}>Sub-menu 2</div>
I'm not sure what the use case is here, but with most menus you want to close the other active sub-menus. For example, if sub-menu 1 is open and you click to open sub-menu 2, you want sub-menu 1 to close and sub-menu 2 to open. Here's how you would achieve that effect:
const [subMenuState, setSubMenuState] = useState([false, false])
const toggleSubMenu = (e, i) => {
e.preventDefault()
// Clone the array
const clone = subMenuState.slice(0)
// Reset all sub-menus except for the one that clicked
const newState = clone.map((val, index) => {
if(index === i) {
return val
}
return false
})
newState[i] = !newState[i]
setSubMenuState(newState)
}
Let me know if you have any questions. Hope this was helpful!
I got my data from firebase, looped through it and displayed it on the frontend. Now I am trying to get the refs value of the already displayed value when I click on it. For example, when I click on Dino, i should be able to see the value 'Dino' on my console tab of Chrome browser.
Here is a link to the picture of the array list displayed on react frontend
<ul>
<li onClick={this.handleSubmit}>
{
Object.keys(this.props.group).map(function(keyName, keyIndex) {
return(
<div key={keyIndex}>{keyName}</div>
)
})
}
</li>
</ul>
Assuming that those "dino, magic & muu" are the keys in this.props.group you need to add an onClick handler to that div, so:
<div key={keyIndex} onClick={() => console.log(keyName)}>{keyName}</div>
You'll need to bind a click event to each div within the li. I would actually rewrite it like so:
<ul>
{
Object.keys(this.props.group).map( (keyName, keyIndex) => {
return(
<li key={keyIndex} onClick={this.handleSubmit.bind(this, keyName}>
{keyName}
</li>
)
})
}
</ul>
Then your handleSubmit function will get the keyName as a param:
handleSubmit(keyName){
// Do something...
}
I have a drop down component that looks like this:
{...}
this.state = {
isVisible: false
}
}
toggleDisplay() {
this.setState({isVisible: !this.state.isVisible});
}
render() {
return (
<div>
<button onClick={this.toggleDisplay()}>click</button>
{this.state.isVisible ? <MenuElements toggleDisplay={this.toggleDisplay} /> : '' }
</div>
)
}
}
"MenuElements" is just a ul that has a li. On another page i am using this component multiple times, so whenever i click on the button, "MenuElements" is shown for each click. The problem is that i want only one component to be displayed. So if a MenuElements component is already displayed, if i click on another button, it closes the previous component, and opens the second one.
How could this be implemented in my code?
Thanks.
You will somehow need to have a single state that defines which MenuItem is displayed. You could go with a global state with something like Redux, but if you are trying to build a reusable component, I guess it'd be best to wrap all of the MenuItem components in a parent component and keep a state there. That, I think, is the React way of doing it. Read this for an idea of how to design components: https://facebook.github.io/react/docs/thinking-in-react.html.
BTW, I think there is an error in the Button onClick handler. It should be:
<button onClick={this.toggleDisplay.bind(this)}> // or bind it somewhere else
Also, the correct way to change state based on previous state is this:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
I'd say this is du to the context of your callbacks. Have you tried forcing the context ?
<div>
<button onClick={this.toggleDisplay.bind(this)}>
click
</button>
{this.state.isVisible ?
<MenuElements toggleDisplay={this.toggleDisplay.bind(this)} />
: '' }
</div>