Dropdown, React Router and back button - reactjs

This small component changes URL when you select something from dropdown. Everything works correctly.. Except back button. Everything else changes if I press it but dropdown doesn't change.
How exactly?
If I go to landing page, default value is All
Now I select Red
Now Blue
Red again
Finally Blue
Now, if I click back button, dropdown always shows last selected value (Blue in my example)
How to overcome this issue?
class MyComponent extends React.Component {
constructor (props) {
super(props)
this.state = {
selected: {
// This only affects dropdown if landing to page
value: this.props.params.color, // Get param from url
label: // Some irrelevant uppercase magic here
}
}
}
static defaultProps = {
colors: [
{value: 'all', label: 'All'},
{value: 'red', label: 'Red'},
{value: 'blue', label: 'Blue'}
]
}
render() {
return (
<Dropdown
options={this.props.colors} {/* All options */}
value={this.props.selected} {/* Selected option */}
onChange={this.handleSelect.bind(this)}
/>
)
}
handleSelect(color) {
this.setState({selected: color})
browserHistory.push(`/${color.value}`)
}
}

Your issue is that you are using state to manage the selected prop of your Dropdown component, however the router does not update this when you navigate back / forward.
Instead remove state entirely from your component and use the router to detect the selected item directly:
import { find } from 'lodash';
class MyComponent extends React.Component {
static defaultProps = {
colors: [
{value: 'all', label: 'All'},
{value: 'red', label: 'Red'},
{value: 'blue', label: 'Blue'}
]
}
getSelected() {
const colours = this.props.colours
const selectedColour = this.props.params.colour
// Find the option matching the route param, or
// return a default value when the colour is not found
return find(colours, { value: selectedColour }) || colours[0];
}
render() {
const selectedOption = this.getSelected();
return (
<div>
<Dropdown
options={ this.props.colors } {/* All options */}
value={ selectedOption } {/* Selected option */}
onChange={ this.handleSelect.bind(this) }
/>
<p>You selected: { selectedOption.label }</p>
</div>
)
}
handleSelect(color) {
browserHistory.push(`/${color.value}`)
}
}

Related

How to disable this icon

import * as React from 'react';
import { IContextualMenuProps, IIconProps, Stack, IStackStyles } from '#fluentui/react';
import { CommandBarButton } from '#fluentui/react/lib/Button';
export interface IButtonExampleProps {
// These are set based on the toggles shown above the examples (not needed in real code)
disabled?: boolean;
checked?: boolean;
}
const menuProps: IContextualMenuProps = {
items: [
{
key: 'emailMessage',
text: 'Email message',
iconProps: { iconName: 'Mail' },
},
{
key: 'calendarEvent',
text: 'Calendar event',
iconProps: { iconName: 'Calendar' },
},
],
};
const addIcon: IIconProps = { iconName: 'Add' };
const mailIcon: IIconProps = { iconName: 'Mail' };
const stackStyles: Partial<IStackStyles> = { root: { height: 44 } };
export const ButtonCommandBarExample: React.FunctionComponent<IButtonExampleProps> = props => {
const { disabled, checked } = props;
// Here we use a Stack to simulate a command bar.
// The real CommandBar control also uses CommandBarButtons internally.
return (
<Stack horizontal styles={stackStyles}>
<CommandBarButton
iconProps={addIcon}
text="New item"
// Set split=true to render a SplitButton instead of a regular button with a menu
// split={true}
menuProps={menuProps}
disabled={disabled}
checked={checked}
/>
<CommandBarButton iconProps={mailIcon} text="Send mail" disabled={disabled} checked={checked} />
</Stack>
)
};
How to disable this icon
in fluent-ui button component
which props or method
i need to add to disable it.
can anyone help regarding it
as i am not able to
get into fluent ui react
https://developer.microsoft.com/en-us/fluentui#/controls/web/button
You can specific the menuIconProps (read here) which will not display the icon if specified as empty string.
Add this property
menuIconProps={{iconName: ""}}
<CommandBarButton
iconProps={addIcon}
text="New item"
// Set split=true to render a SplitButton instead of a regular button with a menu
// split={true}
menuIconProps={{iconName: ""}}
menuProps={menuProps}
disabled={disabled}
checked={checked}
/>
With above change, the button will be displayed as shown below:

React: How to focus on tab after creating?

First of all, I am new to React.
Im using "easyui for React" for my App (Tab Component in my case)
My goals:
When click on a button on the LeftAccordion, a correspoding tab will be added and focus automatically.
When click on a button on the LeftAccordion, focus on the corresponding tab (if already existed)
Problems:
The tabs wouldn't be focused automatically after added. It always focus on the first one (Home) even when the prop selectedTabIndex has been changed (App's state changed).
WHAT I DID:
Click on button [PRODUCT] -> Tab PRODUCT will be added (but not focused)
Click on button [PRODUCT] again -> Tab PRODUCT still not focused
Click on button [CUSTOMER] -> Tab CUSTOMER will be added (but not focused), it still focus on tab HOME.
Now is the thing that made me get mad:
Click on button [PRODUCT] one more time -> Tab PRODUCT will be focused.
Click on button [CUSTOMER] -> Tab CUSTOMER will be focused.
Click on button [INVOICE] -> tab CUSTOMER will be added (but not focused), it still focus on the last one we selected.
Click on any button else on the LeftAccordion, the tab will be focused correctly again.
I knew that the UI will be re-renderred after state changed, but in my case only the tabs would be added, but not get the focus.
Please help! Thank you in advance!
App.js
import React, {useState} from 'react'
import {LinkButton, TabPanel, Tabs} from 'rc-easyui'
import LeftAccordion from "../../components/LeftAccordion";
const App = ({children}) => {
const [data, setData] = useState({
tabs: [{
title: 'HOME', iconCls: 'icon-store-1616', closable: false
}],
selectedTabIndex: 0
}
)
const handleAddTab = (newTab) => {
let clonedTabs = [...data.tabs] //// hoặc dùng data.tabs.slice() => React mới xác nhận có thay đổi giá trị của state
let currentTabs = []
let selectedTabIndex = 0
clonedTabs.forEach(tab => {
currentTabs.push(tab)
})
// check if tab is already exist by Tab's title
let existTab = currentTabs.find(x=>x.title === newTab.title)
if (existTab === undefined) {
currentTabs.push(newTab)
selectedTabIndex = currentTabs.length - 1 // select last tab
}else{
selectedTabIndex = currentTabs.indexOf(existTab)
}
setData(prev => ({
tabs: [...currentTabs],
selectedTabIndex: selectedTabIndex
})
)
}
return(
<>
<LeftAccordion addTabCallback={handleAddTab}></LeftAccordion>
<ContentArea>
<Tabs style={{height:'100%'}} scrollable selectedIndex={data.selectedTabIndex}
onTabClose={()=>{
console.log('close')
}
}
onTabSelect={() => {
console.log('OK')
}}
>
{
data.tabs.map((tab, index) => {
return (
<TabPanel key={index} title={tab.title} closable={tab.closable ? true: false} iconCls={tab.iconCls}>
<p>Tab {index}</p>
</TabPanel>
)
})
}
</Tabs>
</ContentArea>
</>
)
}
export default App
LeftAcordion.js
import React, {Component} from 'react';
import {LinkButton} from 'rc-easyui'
class LeftAccordion extends Component {
constructor(props) {
super(props);
this.state = {
buttons: [
{
text: 'HOME',
iconCls: 'icon-store-1616'
},
{
text: 'PRODUCT',
iconCls: 'icon-product-1616'
},
{
text: 'CUSTOMER',
iconCls: 'icon-customer-1616'
},
{
text: 'INVOICE',
iconCls: 'icon-invoice-1616'
}
]
}
}
render() {
return (
<div className={'leftaccordion-wrapper'}>
<div className={'accordion-splitter-horizontal'}></div>
{
this.state.buttons.map((button, index) => {
return(
<div key={index} className={'accordion-header panel-header f-noshrink'}>
<LinkButton iconCls={[button.iconCls]} plain
onClick={() => {
let tab = {title: button.text, iconCls: button.iconCls, closable: true}
this.props.addTabCallback(tab)
}
}>{button.text}</LinkButton>
</div>
)
})
}
</div>
);
}
}
export default LeftAccordion;

Error - PrimeReact Autocomplete suggestions not showing

https://primefaces.org/primereact/showcase/#/autocomplete
I am trying to load suggestions dropdown as soon as component loads with some data in componentDidMount. The suggestionsList is updating with the obj data in componentDidMount, however suggestion dropdown is not showing.
Simply, whenever input is get focussed and no input text is there, a suggestion dropdown should show.
abcCmp.jsx
class abcCmp extends React.Component {
state;
constructor() {
super();
this.state = {
suggestionsList: []
};
}
componentDidMount() {
let obj = [{'color':'red',name: 'Danny', id: '1'}];
this.setState({suggestionsList: [...obj]})
}
render(){
return (
<div className="containerBox">
<AutoComplete suggestions={this.state.suggestionsList}
minLength={1} placeholder="Add People" field="name" multiple={true}
autoFocus={true} />
</div>
)
}
If you gone through documentation there are other parameters also required.
Those are: completeMethod,value,onChange out of these completeMethod is required to show filtered list as you type. Inside completeMethod you need to filter your data that's how your dropdown list reduces as you type more.
You need ref for toggling dropdown functionality and also you need to check on focus if input value is empty and no value is selected so toggle dropdown.
Here is simple POC I create for reference. Try typing D
Code:
import React from "react";
import { AutoComplete } from "primereact/autocomplete";
import "./styles.css";
let obj = [
{ color: "red", name: "Dagny", id: "1" },
{ color: "red", name: "kanny", id: "2" },
{ color: "red", name: "Dgnny", id: "3" },
{ color: "red", name: "Danny", id: "4" },
{ color: "red", name: "Dmnny", id: "5" },
{ color: "red", name: "Donny", id: "" }
];
class MyComponent extends React.Component {
myRef = React.createRef();
constructor() {
super();
this.state = {
suggestionsList: [],
selected: null,
inputValue: null
};
}
componentDidMount() {
this.setState({ suggestionsList: [...obj] });
}
searchList = (event) => {
let suggestionsList;
if (!event.query.trim().length) {
suggestionsList = [...obj];
} else {
suggestionsList = [...obj].filter((list) => {
return list.name.toLowerCase().startsWith(event.query.toLowerCase());
});
}
this.setState({ suggestionsList });
};
render() {
return (
<div className="containerBox">
<AutoComplete
suggestions={this.state.suggestionsList}
completeMethod={this.searchList}
minLength={1}
ref={this.myRef}
dropdown
inputId="my"
placeholder="Add People"
field="name"
onFocus={(e) => {
if (!this.state.inputValue && !this.state.selected) {
this.myRef.current.onDropdownClick(e, "");
}
}}
onKeyUp={(e) => this.setState({ inputValue: e.target.value })}
// completeOnFocus={true}
multiple={true}
autoFocus={true}
value={this.state.selected}
onChange={(e) => this.setState({ selected: e.value })}
/>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<MyComponent />
</div>
);
}
Demo: https://codesandbox.io/s/prime-react-autocomplete-forked-n3x2x?file=/src/App.js
Add dropdown inside autocomplete tags and also add completeMethod and bind it to a search/filter function, add a value to bind the selected value, add a onChange function to it
You can find full documantation and working example here :PrimeFaces React Autocomplete

Adding new options to form on click in ReactJs

I am doing a React search where user can add multiple filters. The idea is that at first there is only one filter (select and input field) and if user wishes to add more, he can add one more row of (select and input) and it will also take that into account.
I cannot figure out the part on how to add more rows of (select, input) and furthermore, how to read their data as the list size and everything can change.
So I have multiple options in the select array:
const options = [
{ label: "foo", value: 1 },
{ label: "bar", value: 2 },
{ label: "bin", value: 3 }
];
Now if user selects the first value from the Select box and then types a text in the input box I will get their values and I could do a search based on that.
const options = [
{ label: "foo", value: 1 },
{ label: "bar", value: 2 },
{ label: "bin", value: 3 }
];
class App extends React.Component {
state = {
selectedOption: null,
textValue: null
};
handleOptionChange = selectedOption => {
this.setState({ selectedOption: selectedOption.value });
};
handleTextChange = event => {
this.setState({ textValue: event.target.value });
};
handleSubmit = () => {
console.log(
"SelectedOption: " +
this.state.selectedOption +
", textValue: " +
this.state.textValue
);
};
addNewRow = () => {
console.log("adding new row of filters");
};
render() {
const { selectedOption } = this.state;
return (
<div>
<div style={{ display: "flex" }}>
<Select
value={selectedOption}
onChange={this.handleOptionChange}
options={options}
/>
<input
type="text"
value={this.state.textValue}
onChange={this.handleTextChange}
/>
</div>
<button onClick={this.addNewRow}>AddNewRow</button>
<button onClick={this.handleSubmit}>Submit</button>
</div>
);
}
}
export default App;
I have also created a CodeSandBox for this.
If user clicks on the addNewRow a new row should appear and the previous (search, input) should be selectable without the row that was previously selected.
I don't even really know how I should approach this.
To add new row of inputs on click of button you need to add new input item into the list of inputs, like I have mention below::
import React, { Component } from 'react'
import Select from "react-select";
const options = [
{ label: "foo", value: 1 },
{ label: "bar", value: 2 },
{ label: "bin", value: 3 }
];
class App extends Component {
constructor(props) {
super(props);
this.state = { inputGroups: ['input-0'] };
}
handleSubmit = () => {
console.log("form submitted");
};
AddNewRow() {
var newInput = `input-${this.state.inputGroups.length}`;
this.setState(prevState => ({ inputGroups: prevState.inputGroups.concat([newInput]) }));
}
render() {
return (
<div>
<div>
<div>
{this.state.inputGroups.map(input =>
<div key={input} style={{ display: "flex" }}>
<Select
options={options}
/>
<input
type="text"
// value={this.state.textValue}
// onChange={this.handleTextChange}
/>
</div>
)}
</div>
</div>
<button onClick={() => this.AddNewRow()}>AddNewRow</button>
<button onClick={this.handleSubmit()}>Submit</button>
</div>
);
}
}
export default App;
After click on "AddNewRow" button it will add new input group for you. Now you need to wrap this inputGroup inside "Form" to get data of each inputGroup on click of submit.
I hope it will resolve your issue.

ToggleCollapsed on imported sidebar reactjs component

Using a sidebar component from [office-ui-fabric-react][1].
I want to sidebar to start in the Collapsed state, but I am having difficulty setting the state of the ISidebar interface to do this.
How can I toggleCollapsed on this imported component?
export interface ISidebar {
/**
* Toggles the sidebar state to put the sidebar in or out of collapsed mode
* #type {(boolean) => void}
*/
toggleCollapsed: () => void;
}
export class SidebarExample extends React.Component {
public render(): JSX.Element {
this.state = {
active: true
};
return (
<Sidebar
id={'sidebar-collapsed'}
collapsible={true}
theme={getTheme()}
/>
)
Public methods (e.g. toggleCollapsed) of Sidebar component are accessible through componentRef, for example:
<Sidebar componentRef={initSidebar} />
const initSidebar = (sideBar: ISidebar) => {
sideBar.toggleCollapsed();
};
Example
The initial collapsed state could be set like this:
const initSidebar = (ref: ISidebar) => {
ref.setCollapsed(true);
};
const SidebarBasicExample: React.SFC<{}> = props => {
return (
<Sidebar
componentRef={initSidebar}
collapsible={true}
id={"1"}
theme={getTheme()}
items={[
{
key: "basic-example-item1",
name: "Item 1",
iconProps: { iconName: "BuildQueue" },
active: false
},
{
key: "basic-example-item2",
name: "Item 2",
iconProps: { iconName: "Bullseye" },
active: true
}
]}
/>
);
};
With Vadim's help, my answer.
import { getTheme } from 'office-ui-fabric-react';
import * as React from 'react';
import { Sidebar, ISidebar } from '#uifabric/experiments/lib/Sidebar';
const initSidebar = (sideBar: ISidebar) => {
sideBar.toggleCollapsed();
};
export class SidebarCollapsibleExample extends React.Component {
public render(): JSX.Element {
this.state = {
active: true
};
return (
<Sidebar
id={'sidebar-collapsed'}
collapsible={true}
theme={getTheme()}
collapseButtonAriaLabel={'sitemap'}
componentRef={initSidebar}
items={[
{
key: 'collapsible-example-item1',
name: 'Item 1',
iconProps: { iconName: 'BuildQueue' },
active: false
} ]}
/>
);
}
}

Resources