Logic for Three-dot more options menu for web applications in reactjs - reactjs

I am new to ReactJs and I'm developing a social media web application. Here I have template where I have to implement a Three-dot more options menu. I tried using Bootstrap menu and react Bootstrap component menu. Both didn't work for me. What is the best way to implement this feature without using a library?
I did till toggling the menu. But on click, all the menus toggle altogether. I am not able do the toggling individually.
Here's the piece of code I did:
post.jsx
class UserPost extends Component {
state = {
overFlowMenuActive: false
};
toggleOverflowMenu = () => {
this.setState((prevState) => ({ overFlowMenuActive:
!prevState.overFlowMenuActive }));
};
closeOverflowMenu = () => {
this.setState({ overFlowMenuActive: false });
};
render() {
return (
<React.Fragment>
{this.props.posts.map((post, index) =>(
<div>
<div tabIndex='0' onBlur={this.closeOverflowMenu}>
<img src={require('../../assets/images/more.svg')} alt='' onClick={this.toggleOverflowMenu}/>
</div>
<MoreBtn options={this.state.options} overFlowMenuActive={this.state.overFlowMenuActive} />
</div>
))}
</React.Fragment>
);
}
MoreBtn.jsx
<div className={`${classes['popup-more']} ${this.props.overFlowMenuActive
? classes.expand
: classes.collapse}`}>
{this.props.options.map((option, index) => (
<div key={index}>
<img src={option.url} alt='' />
<p>{option.name}</p>
</div>
))}
</div>

You are maintaining only a single state for all UserPosts
To have each of these toggle seperately, these states should be moved into the component.
class SinglePost extends Component {
state = {
overFlowMenuActive: false
};
toggleOverflowMenu = () => {
this.setState((prevState) => ({ overFlowMenuActive:
!prevState.overFlowMenuActive }));
};
closeOverflowMenu = () => {
this.setState({ overFlowMenuActive: false });
};
render() {
return (
<div>
<div tabIndex='0' onBlur={this.closeOverflowMenu}>
<img src={require('../../assets/images/more.svg')} alt='' onClick={this.toggleOverflowMenu}/>
</div>
<MoreBtn options={this.state.options} overFlowMenuActive={this.state.overFlowMenuActive} />
</div>
);
}
class UserPost extends Component {
render() {
return (
<React.Fragment>
{this.props.posts.map((post, index) =>(
<SinglePost post={post} />
))}
</React.Fragment>
);
}
This way, the button for only one component is toggled at a time

Related

How to change a style of an HTML element in React?

I have two React components
class App extends React.Component {
render() {
return (
<div id="appWrapper">
<ConfigureWindow />
<button id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
}
const ConfigureWindow = () => (
<div id="configureWindowWrapper">
<div id="configureWindow">
<section id="addCitySection">TODO: adding a city</section>
<div id="verticalLine"></div>
<section id="listOfCities">
<header>
<h1>Available cities</h1>
<div id="closeConfigureWindowWrapper">
<img src="..\src\images\exit.png" id="closeConfigureWindow" alt="" />
</div>
</header>
<section id="availableCities"></section>
</section>
</div>
</div>
);
I want "ConfigureWindow" to be shown when "configureClocksButton". I tried to execute it with props, state and a function but got errors. It also would be nice if you explain me how to create new React components with React functions?
You probably want to use the React.JS event onClick (https://reactjs.org/docs/handling-events.html), and a state to store the action. To create a function component, you just have to return the JSX you want to render, and use hooks (https://reactjs.org/docs/hooks-intro.html) and then do a conditional rendering (https://reactjs.org/docs/conditional-rendering.html):
const App = () => {
const [toggleConfiguration, setToggleConfiguration] = useState(false)
return (
<div id="appWrapper">
{toggleConfiguration && <ConfigureWindow />}
<button onClick{() => setToggleConfiguration(true)} id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
It's a bit difficult to understand your post, but I gather you want to click the button with id="configureClocksButton" and conditionally render the ConfigureWindow component.
You can accomplish this with some boolean state, a click handler to toggle the state, and some conditional rendering.
class App extends React.Component {
this.state = {
showConfigureWindow: false,
}
toggleShowConfigureWindow = () => this.setState(prevState => ({
showConfigureWindow: !prevState.showConfigureWindow,
}))
render() {
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={this.toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
}
A function component equivalent:
const App = () => {
const [showConfigureWindow, setShowConfigureWindow] = React.useState(false);
const toggleShowConfigureWindow = () => setShowConfigureWindow(show => !show);
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}

How can I get elements from one component in another one by id or value?

I've created a component to create follow and unfollow buttons and now I want to use this component in other components (like Suggestions).
In the suggestions component I want to show only the button that its value is equal to the user.id, but I am only able to get the 5 buttons from the original component.
Is there a way to select only the button that is equal to the user.id?
This is the component that creates the buttons:
render() {
const { users, followingUsers } = this.state
const userId = this.props.user[0].id
return(
<div>
{users.map((user, index) => {
if(userId !== user.id) {
if(followingUsers.includes(user.user_name)) {
return(
<Button key={index} value={user.id} onClick={this.onUnfollow}>Unfollow</Button>
)
} else {
return(
<Button key={index} value={user.id} onClick={this.onFollow}>Follow</Button>
)
}
}
})}
</div>
)
}
}
export default withUser(Unfollowfollow);
Here is the suggestions component:
render() {
const { users } = this.state
const userId = this.props.user[0].id
return (
<div>
<ul>
{users.map((user, index) => {
if(user.id !== userId) {
return (
<Card className="users" key= {index}>
<CardBody>
<CardImg className="picfollowers" top width="9%" src={user.image} />
<CardTitle onClick={() => this.handleClick(user.id)}>{user.user_name}</CardTitle>
<Unfollowfollow />
</CardBody>
</Card>
)}
})}
</ul>
</div>
)
}
}
export default withUser(Suggestions);

Re-render component based on object updating

I have the following pattern
class List {
list: string[] = [];
showList() {
return this.list.map(element => <div>{element}</div>);
}
showOptions() {
return (
<div>
<div onClick={() => this.addToList('value1')}>Value #1</div>
<div onClick={() => this.addToList('value2')}>Value #2</div>
<div onClick={() => this.addToList('value3')}>Value #3</div>
<div onClick={() => this.addToList('value4')}>Value #4</div>
</div>
);
}
addToList(value: string) {
this.list.push(value);
}
}
class App extends Component {
myList: List;
constructor(props: any) {
super(props);
this.myList = new List();
}
render() {
<div>
Hey this is my app
{this.myList.showOptions()}
<div>{this.myList.showList()}</div>
</div>
}
}
It shows my options fine, and elements are added to the list when I click on it. However, the showList function is never called again from App, thus not showing any update.
How can I tell the main component to rerenders when List is updated ? I'm not sure my design pattern is good. My goal is to manage what my class displays inside itself, and just call the display functions from other components.
We should always use state to rerender react component.
Not sure what you want to accomplish exactly but hopefully this will give you a general idea what Jim means with using state:
const Option = React.memo(function Option({
value,
onClick,
}) {
return <div onClick={() => onClick(value)}>{value}</div>;
});
const Options = React.memo(function Options({
options,
onClick,
}) {
return (
<div>
{options.map(value => (
<Option
key={value}
value={value}
onClick={onClick}
/>
))}
</div>
);
});
class List extends React.PureComponent {
state = {
options: [1, 2, 3],
selected: [],
};
showList() {
return this.list.map(element => <div>{element}</div>);
}
add = (
value //arrow funcion to bind this
) =>
this.setState({
options: this.state.options.filter(o => o !== value),
selected: [...this.state.selected, value],
});
remove = (
value //arrow funcion to bind this
) =>
this.setState({
selected: this.state.selected.filter(
o => o !== value
),
options: [...this.state.options, value],
});
render() {
return (
<div>
<div>
<h4>options</h4>
<Options
options={this.state.options}
onClick={this.add}
/>
</div>
<div>
<h4>choosen options</h4>
<Options
options={this.state.selected}
onClick={this.remove}
/>
</div>
</div>
);
}
}
const App = () => <List />;
//render app
ReactDOM.render(
<App />,
document.getElementById('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="root"></div>

Cannot read property 'tabsDivIframe' of undefined - React

I am creating an application with tabs and divs to show the iframes or divs associated with the tabs. I have a navigation menu that works perfectly, when you click on one of the menu items you create a new tab and at the same time you should create a div / iframe (as applicable). The creation of the div is failing in my DivAndIframe class, it gives this error Can not read property 'tabsDivIframe' of undefined when I try to paint <DivAndIframe tabsDivIframe {this.props.divIframe.tabsDivIframe} />. It does not make sense because in my class App is an array with content that does not throw any errors.
class App extends Component {
constructor(props, context){
super(props, context);
["openTabs"].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
tabs:{
tabsLi: [],
},
divIframe:{
tabsDivIframe: [],
},
showtabs: true,
}
}
openTabs(e, url, iframe, trdtitle){
e.preventDefault();
//Change the state
this.setState({
showtabs: false,
})
//Creating tabs + iframe/div
if (this.state.tabs.tabsLi.includes(trdtitle) === false){
this.setState({
tabs: { tabsLi:[...new Set(this.state.tabs.tabsLi),trdtitle].filter(function(el) { return el; })},
divIframe: { tabsDivIframe:[...new Set(this.state.divIframe.tabsDivIframe),url].filter(function(el) { return el; })},
}, () => {
//this.state.divIframe.tabsDivIframe is an array
console.log(this.state.tabs.tabsLi);console.log(this.state.divIframe.tabsDivIframe)
})
}
}
render(){
return (
<>
<section className='section'>
<Tabs
navigation={this.state.navigation}
textvalue={this.state.textvalue}
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
openTabs={this.openTabs}
removeTab={this.removeTab}
/>
</section>
</>
)
}
}
class Tabs extends Component {
render(){
return(
<div id="content-tabs" className="tabs">
{( this.props.showtabs)
? (
<>
<div className="waiting-leads">
<p>Parece que todavía no hay ningún lead...</p>
<h3>¡Ánimo, ya llega!</h3>
<img src={imgDinosaurio} alt="Dinosaurio"></img>
</div>
</>
) : (
<>
<ul id="resizable" className="content" >
<LiTabs
tabsLi={this.props.tabs.tabsLi}
removeTab={this.props.removeTab}
/>
</ul>
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
/>
</>
)}
</div>
);
}
}
class LiTabs extends Component{
render(){
return(
<>
{this.props.tabsLi.map((value, index) =>
<li key={index}>
<span>{value}</span>
</li>
)}
</>
);
}
}
class DivAndIframe extends Component{
render(){
return(
<>
{this.props.tabsDivIframe.map((url, index) =>
<div key={index}>
<span>Test {url}</span>
</div>
)}
</>
);
}
}
I do not understand why DivAndIframe does not work when it is exactly the same as LiTabs
I think you have a typo.
When rendering Tabs, in App, you pass the props:
<Tabs
navigation={this.state.navigation}
textvalue={this.state.textvalue}
showtabs={this.state.showtabs}
tabs={this.state.tabs}
tabsLi={this.state.tabs.tabsLi}
tabsDivIframe={this.state.divIframe.tabsDivIframe}
openTabs={this.openTabs}
removeTab={this.removeTab}
/>
And inside Tabs you have:
<DivAndIframe
tabsDivIframe={this.props.divIframe.tabsDivIframe}
/>
You aren't passing divIframe to Tabs and that is why you are getting Can not read property 'tabsDivIframe' of undefined. this.props.divIframe is undefined.
Maybe it should be other name?
Like this.props.tabsDivIframe ?

How to pass ref of component up three levels for scroll event?

I am using the <InfiniteScroll/> component in my code like this:
<div style="height:700px;overflow:auto;" ref={(ref) => this.scrollParentRef = ref}>
<div>
<InfiniteScroll
pageStart={0}
loadMore={loadFunc}
hasMore={true || false}
loader={<div className="loader" key={0}>Loading ...</div>}
useWindow={false}
getScrollParent={() => this.scrollParentRef}
>
{items}
</InfiniteScroll>
</div>
</div>
I need to pass the from ref = {(ref) => this.scrollParentRef = ref; } to the component's grandparent, however my code does not work as expected. Here is my full component source code:
class AppEmpty extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.props.setReferences(this.scrollParentRef);
}
render() {
const { children } = this.props;
const isActive = this.props.isActive;
return (
<div className="App">
<NavBar/>
<MenuRight/>
<div id="content"
className={isActive ? "content_enable": ""}
style={this.props.menuRight.styleContent}
ref={ (ref) => this.scrollParentRef = ref }
>
<FlashMessages/>
{children}
</div>
</div>
);
}
}
AppEmpty.protoTypes = {
children: PropTypes.element.isRequired,
};
function mapsStateToProps(state){
return {
menuRight: state.menuRight,
isActive: state.isActive,
};
}
export default connect (mapsStateToProps, {setReferences})(AppEmpty);
The documentation says:
getScrollParent Function Override method to return a different scroll listener if it's not the immediate parent of InfiniteScroll.
Can someone suggest a way to pass that reference in an easy way to the <InfiniteScroll/>?
After trying everything, I realized that I can pass as a refs using document.getElementById ('comp_id_a_referenciar') :(
Content-> compomente_base -> form_busqueda, Tables -> InfiniteScroll(Refs_Content).
<div>
<InfiniteScroll
pageStart={0}
loadMore={loadFunc}
hasMore={true || false}
loader={<div className="loader" key={0}>Loading ...</div>}
useWindow={false}
getScrollParent={ () => document.getElementById('content') }
>
{items}
</InfiniteScroll>
working! nice!

Resources