Close SelectField menu with multiple=true - reactjs

I am trying to close from the outside a SelectField. The idea is that I have a multiple selectField (which doesn't close on change by definition) with a "Clear All" item in the drop down menu that deselects all items on click.
The code I have is similar to this:
class MultiSelect extends Component {
state = {values: []}
clear = () => {
this.setState({values: []});
// FixMe: close the DropDownMenu here.
};
handleChange = (e,i,v) => {
this.setState({values: v});
};
render() {
const {title, choices, style} = this.props;
const {values} = this.state
return <div style={{...style}}>
<SelectField
floatingLabelText={title}
multiple
value={values}
onChange={this.handleChange}
>
<MenuItem value={null} primaryText="Clear All" onTouchTap={this.clear}/>
<Divider/>
{choices.map((d,i) =>
<MenuItem key={i} value={d} primaryText={d} checked={values.includes(d)}/>
)}
</SelectField>
</div>
}
}
The best way of achieving this would be to call the DropDownMenu.close() function. Using the newly introduced dropDownMenuProps prop of SelectField I thought I could do this, however the function apparently gets overwritten in DropDownMenu.
So, is there another way to achieve this or should I implement this component from scratch using the DropDownMenu directly?

Related

Defining Components in React

I am creating a react website in which many pages have a specific button that should look the same for all. Should this button be its own component? If so, how would I specify the onClick events to be different for each button if it is a component?
Yes, it should be its own component.
Create it in a separate file so you can import them where you need to use.
The component should receive a onClick prop that you pass to the internal button tag.
See this example: https://reactjs.org/docs/components-and-props.html#composing-components
export const Button = ({ label, onClick, disabled }) => {
return (
<button
onClick={onClick}
disabled={disabled}
>
{label}
</button>
)
}
and then you can export this component inside any of the components you want, and pass in values like onClick and label to make it more dynamic
export const DemoFunction () => {
const onClickHandler = () => {}
return (
<Button label="Click" onClick={onClickHandler} />
)
}

How to pass state from parent to child in react?

How do I pass a state attribute from parent to child? In the following implementation, the Dropdown component has a state "isActive" and I want to access it in the Button component to attach propper styling to it. The Dropdown has to generic as it is supposed to take different sorts of buttons.
<Dropdown items="...">
<Button active ="false" />
</Dropdown>
Dropdwon.js
...
constructor(props){
super(props)
this.state = {
isActive: true,
}
}
render (){
return (
<div className={styles.toggle} onClick={(event) => this.showMenu(event)}>
{this.props.children} /* want to set active prop for the child button here */
</div>
);
}
...
You have two possibilities:
Lift your Dropdown state and keep it in its parent component;
Use useContext hook;
The first approach would be better, but it may not be good for your application (I cannot know that). Let me make an example for both cases.
This is an example where I've lifted the isActive state to the parent component.
const ParentComponent = () => {
const [isActive, setIsActive] = useState(false);
handleIsActiveChange = (newValue) => {
setIsActive(newValue);
}
<Dropdown isActive={isActive} setIsActive={handleIsActiveChange}>
<Button isActive={isActive} />
</Dropdown>
}
const Dropdown = props => {
// You can use `props.isActive` to know whether the dropdown is active or not.
// You can use `props.handleIsActiveChange` to update the `isActive` state.
}
const Button = props => {
// You can use `props.isActive` to know whether the dropdown is active or not.
}
Instead, this exploits the useContext API:
const dropdownContext = React.createContext(null);
const Dropdown = props => {
const [isActive, setIsActive] = useState(false);
return (
<dropdownContext.Provider value={{ isActive }}>
{props.children}
</dropdownContext.Provider>
);
}
const Button = props => {
const dropdownCtx = React.useContext(dropdownContext);
// You can use `dropdownCtx.isActive` to know whether the dropdown is active or not.
}
Aside from the answer I linked, there might be another way of achieving this that I didn't see mentioned.
You can send a function as a children element of your dropdown which will take isActive as a variable :
<Dropdown items="...">
{isActive => <Button active={isActive} />}
</Dropdown>
Then, is the render function, simply call the function and send your state value as a parameter :
render(){
return (
<div className={styles.toggle} onClick={(event) => this.showMenu(event)}>
{this.props.children(this.state.isActive)}
</div>
);
}
<Dropdown >
<Button isActive={this.state.isActive} />
</Dropdown>
In your button get it with this.props.isActive

How to show a block of collapsible text on click of button

I am trying to implement a collapsible component. I have designed it such as, on click of a button, a block of dynamic text will appear. I made a functional component and using the tags in a class. The name of the component is, CustomAccordion.jsx and using this component in Container.jsx
I have tried to create a button and a function for onClick event.
Part of the CustonAccordion.jsx
const handleToggle = () : string =>{
let content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
}else{
content.style.maxHeight = content.scrollHeight +'px';
}
}
export default function CustomAccordion(props: PropType): React.Component<*> {
const { title, children } = props
return(
<div>
<AccordionButton onClick={() => this.handleToggle()}>{title}</AccordionButton>
<AccordionContent>
<p>{children}
</p>
</AccordionContent>
</div>
)
}
Part of calling Container.jsx
<CustomAccordion title = {this.props.name}>
<p>This is the text passed to component.</p>
</CustomAccordion>
<br />
This does not show the expanded text and it seems that the click event does not work properly. I am very new in react, guessing the syntax might be incorrect.
In react you should generally try to avoid touching DOM directly unless you really have to.
Also you are accessing the handleToggle function wrongly. It should be onClick={() => handleToggle()} because this in your case is window/null and so it has no handleToggle method.
Instead you can use a stateful class component to achieve the same thing.
export default class CustomAccordion extends React.Component {
state = {show: false};
toggle = () => this.setState({show: !this.state.show});
render() {
const {title, children} = this.props;
const {show} = this.state;
return (
<div>
<AccordionButton onClick={this.toggle}>{title}</AccordionButton>
{show && (
<AccordionContent>
<p>{children}</p>
</AccordionContent>
)}
</div>
)
}
}
If you want to have some kind of animation, you can set different className based on the show state instead of adding/removing the elements.

semantic-ui-react List onClick declaration

i'm trying to create a list of documents dynamically with semantic-ui-react. I'd like to get the document title back when the list item is clicked. According to the documentation:
https://react.semantic-ui.com/elements/list
there is an onItemClick prop for this reason, however when im using it i get a warning when it's rendered:
Warning: Failed prop type: Prop onItemClick in List conflicts with props: children. They cannot be defined together, choose one or the other.
Also clicking on the list item does nothing (atm i just want to log the doc title to the console). Here is the code:
handleListItemClick(event, data) {
console.log("list item clicked: " + data.value);
}
buildResultsContainer() {
return this.props.listOfResults.map((document,index) =>
{
return (
<List.Item
as='a'
key={index}>
<Icon name='file' />
<List.Content>
<List.Header>{document.properties.title}</List.Header>
<List.Description>
{document.properties.description}
</List.Description>
</List.Content>
</List.Item>
);
}
);
}
render() {
return (
<div>
<List onItemClick={this.handleListItemClick}>
{this.buildResultsContainer()}
</List>
</div>
)
}
Can you please tell me how to use properly the onItemClick prop for the List component?
Less important, do you have any tip how to refactor the list rendering? Just wanted to keep the render function short and clean, but this function call looks a bit overkill....
Thanks a lot!
I think maybe the intent when using onItemClick is that you would use the items prop on List since then you wouldn't have any children e.g.
render() {
const items = this.props.listOfResults.map(document => {
return {
icon: 'file',
content: document.properties.title,
description: document.properties.description,
onClick: e => console.log(document.title)
}
});
return <List items={items} />
}
If you had your listOfResults prop in the above format, you wouldn't even need to do this map and your render function would be super tight:
render() {
return <List items={this.props.listOfResults} />;
}
Alternately, List.Item takes an onClick prop that you could define in your buildResultsContainer() function. Because each onClick function is unique based on the current document object, you will need to use an anonymous function to call your handleClick function as follows:
<List.Item
onClick={() => this.handleClick(document.title)}
...etc
/>
You would then have:
handleClick = docTitle => {
console.log(docTitle);
};
If what you wanted was obtainable from event.target, you could just pass the reference of handleClick to the onClick i.e.
handleClick = event => {
console.log(e.target.innerText);
};
<List.Item
onClick={this.handleClick}
/>

Can't get button component value onClick

I'm sure this is something trivial but I can't seem to figure out how to access the value of my button when the user clicks the button. When the page loads my list of buttons renders correctly with the unique values. When I click one of the buttons the function fires, however, the value returns undefined. Can someone show me what I'm doing wrong here?
Path: TestPage.jsx
import MyList from '../../components/MyList';
export default class TestPage extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleButtonClick = this.handleButtonClick.bind(this);
}
handleButtonClick(event) {
event.preventDefault();
console.log("button click", event.target.value);
}
render() {
return (
<div>
{this.props.lists.map((list) => (
<div key={list._id}>
<MyList
listCollection={list}
handleButtonClick={this.handleButtonClick}
/>
</div>
))}
</div>
);
}
}
Path: MyListComponent
const MyList = (props) => (
<div>
<Button onClick={props.handleButtonClick} value={props.listCollection._id}>{props.listCollection.title}</Button>
</div>
);
event.target.value is for getting values of HTML elements (like the content of an input box), not getting a React component's props. If would be easier if you just passed that value straight in:
handleButtonClick(value) {
console.log(value);
}
<Button onClick={() => props.handleButtonClick(props.listCollection._id)}>
{props.listCollection.title}
</Button>
It seems that you are not using the default button but instead some sort of customized component from another libray named Button.. if its a customezied component it wont work the same as the internatls might contain a button to render but when you are referencing the event you are doing it throug the Button component

Resources