How can I replace component's state with react? - reactjs

I have a local state of currentMenu in component of MenuItemContainer
export default class MenuItemsContainer extends React.Component {
constructor () {
super();
this.state = {
currentMenu: [],
};
}
I render menu_items by using function_renderMenuItem like below,
_renderMenuItems(menuitems) {
const { order } = this.props;
return menuitems.map((menuitem) => {
if (menuitem.category_id == this.props.order.currentCategoryId) {
this.state.currentMenu.push(menuitem)
else {
return false;
}
return <MenuItem
key={menuitem.id}
order={order}
dispatch={this.props.dispatch}
channel={this.props.order.channel}
{...menuitem} />;
});
}
What I want to do with currentMenu is that storing menuItems which category_id of menuItem equals to currentCategoryId of order state.
Now I am using push(menuitem) to push those items to the state. However, in currentMenu, it should store only if when category_id of menuItem is equal to currentCategoryId of orders state. So when there are changes of currentCategoryId, it should reset currentMenu and get new menuItem
How can I do this?
Thanks in advance

In order to actually trigger the state change you need to do this via setState
so you can add a setState command at the end of your _renderMenuItems method like so:
this.setState({currentMenu: state.currentMenu})
But the better practice is to have the state already containing the right items and not filter it in the render method.
Im not sure from your example how you are getting the menuItems, but in theory you should build the state from them and then call the _renderMenuItems method (that will use the state)

Related

conditionally disable an option using react select not work when change option props passed to the Select

I'm trying to conditionally make an option of a select disabled, for example, if two items are selected, the the third option is diabled, if one or three items are selected, the third option is enabled. However the Select option does not change when selected number of items change even it's passed as props of the option. When I console.log, I do see the option object disabled value changed when I change selected items numbers. May I know why the Select does not re render? Thanks!
class parentComponent extends PureComponent{
constructor(props) {
super(props)
this.state = {
options:[]
}
}
render() {
const {
countOfItems
} = this.props
let options = [...somefunction()];
if (somecondition1) {
options.options.filter(...)
}
if (somecondition2) {
options.options.filter(...)
}
this.setState({options})
......
if (countOfItems === 2) {
options[3].disabled = true
} else {
options[3].disabled = false
}
console.log(options[3])
......
return (
<Select
options ={options}
isOptionDisabled = {(option) => option.disabled}
......
)
}
}
React component render their UI/DOM after DOM diffing which causes the component to make a new reference of return statement and render updated changes to UI. which is triggered by useSTATE hook. If you are not updating state. No dom diffing occur. So you'll have the old reference of Dom.
You can do it this way
class Options {
this.state={options:[]}
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.countOfItems !== prevProps.countOfItems) {
let tempOptions = [...this.state.options]
if (this.props.countOfItems === 2) {
tempOptions[3].disabled = true
} else {
tempOptions[3].disabled = false
}
this.setState({options:tempOptions})
}
}
render() {
......
return (
<Select
options ={this.state.options}
isOptionDisabled = {(option) => option.disabled}
......
)
}

How to manage a variable length array in React state

I may be thinking about this the wrong way, so I appreciate any redirection on my design.
I have a React component which gets a list in props. The list can be from 0 to n in length. I want to to manage an attribute of each list item in the state of the React component. (example below) My gut tells me I'm doing something wrong, since I'm trying to set the state's value via props.
Is there a proper way to accomplish what I'm trying here?
class MyList extends React.Component {
state = {
listItems: {}
}
render(){
return(
{this.renderListItems(this.props.list)}
)
renderListItems = list => {
return list.map( listItem => {
let id = listItem.id
return <ListItem key={id} listItem={listItem} color={this.state.listItems[id].color} />
}
}

How can make component level variable as constant in ReactJS?

I want to have a component level variable as constant in ReactJS, Redux with Typescript.
export class MyComponent {
initialProps = {};
constructor() {
}
componentWillMount() {
this.initialProps = this.props; //Early tried without this. But this.props is getting changed because of the filter obj.name in the below function.
}
render() {
return(
<MyPanel>
//Another <select> widget
{this.getCustomObject()}
</MyPanel>
);
}
getCustomObject() {
if(this.props.selectedId == 2) {
let filteredTypes = [];
this.initialProps.obj.forEach((o) => {
if(this.props.obj.name !== 'ABC'){
filteredTypes.push(this.props.obj);
}
});
this.props.types = filteredTypes;
}
return this.props;
}
Note: Actually i want to do re-render the component and display the filtered object based on the dropdown selection. But once we filtered. Next time when we change the dropdown value we are getting the filtered the value in the props.
But we need the props which was passed first time to this componennt and filter the values everytime. SO tried with this.initialProps. But that also getting changed once this.props is updated.

How to observe object.property changes on an observable array with mobx

I'm using reactjs and mobx. I have an observable array of Item objects, and I'm trying to display them and show property changes "live" by observing the properties on the objects in the array. The changes are not triggered by a click event on any of the components, but by a response to an API call.
I understand that property changes on objects within the array will not trigger the entire list to re-render (which is good), but I can't get it to re-render the single Item component that should be observing the properties of the Item object.
I have tried a couple methods of getting the Item objects within the array to be observable, but none of these are working for me:
calling 'extendObservable() from the Item's constructor
assigning the props.item to a class member decorated with '#observable'
calling the observable constructor and passing in the item object like this: const item = observable(item)
passing the 'hasUnreadData' field as a separate prop and making that observable via 'observable.box(item.hasUnreadData).
Here's some simplified example code (in typescript):
class Item {
id : string
hasUnreadData : boolean
constructor (data: any) {
this.id = data.id;
// false by default
this.hasUnreadData = data.hasUnreadData;
}
}
#observable items: Item[];
// observes the array and re-renders when items are added/removed (this works)
#observer
class ItemListComponent extends React.Component {
render() {
return (
<List> {
items.map((item: Item, index) => {
<ItemComponent key={item.id} itemModel={item} />
}
}
)
}
}
// should observe the 'hasUnreadData' flag and apply different styles when it re-renders (but this does not work, it only displays the initial state)
#observer
class ItemComponent extends React.Component {
render() {
const item = this.props.item;
return (
<ListItem button divider selected={item.hasUnreadData} />
)
}
}
// imagine this is a promise from an API call
API.fetchData().then((itemId: string) => {
itemToUpdate = items.find(i => i.id === itemId);
itemToUpdate.hasUnreadData = true;
// this does not trigger the ItemComponent to render again as expected.
});
Do I need to clone or otherwise "re-create" the Item object to trigger render? Or am I making come kind of obvious mistake here? Any help appreciated.

Change items in list in React when an item has been clicked

I'm quite new to ReactJS and I have trouble understand how different components can communicate with each other.
I do have a component that will render a list and each list item is a different component. I want to keep the components as small as possible.
Now, each list item can have a property named active and if the property is set to true, an additional class is added.
This is the class that defines a single item in the component.
See this below code for my component defining a single list item:
export default class OfficeRibbonTab extends React.Component {
constructor(props) {
super(props);
this.state = {
active: props.active ? props.active : false
}
// Assign all the correct event handlers.
this.setActive = this.setActive.bind(this);
}
setActive() {
this.setState({active: true});
}
render() {
// When the tab is defined as active, add the "active" class on it.
if (this.state.active)
{ var tabClassName = "active"; }
return <li onClick={this.setActive} className={tabClassName}>{this.props.tabName}</li>;
}
}
So, I have propery active which is passed to this component, which I store in the components state.
When I click the list item, I set to state of the current item to be active.
The problem is that I want all the other list items to become inactive, thus setting the state of active to false.
The code below is an overview of my list:
export default class OfficeRibbon extends React.Component {
constructor(props) {
// Call the 'base' constructor.
super(props);
}
render() {
var tabs = [];
// Loop over all the tab elements and define how the element should be rendered.
for (var i = 0; i < this.props.dataSource.tabs.length; i ++)
{
if (i == 1)
{ tabs.push(<OfficeRibbonTab active={true} key={this.props.dataSource.tabs[i].name} tabName={this.props.dataSource.tabs[i].name}></OfficeRibbonTab>); }
else
{ tabs.push(<OfficeRibbonTab key={this.props.dataSource.tabs[i].name} tabName={this.props.dataSource.tabs[i].name}></OfficeRibbonTab>); }
}
return (<div>
<div className="wrap-top">
<OfficeRibbonTitle title={this.props.title}/>
<ul className="tabs">
{tabs}
</ul>
</div>
</div>);
}
}
It doesn't seem like rocket science, but I want to do it the React way without re-inventing the wheel.
Anyone who can guide me in the right direction?
Kind regards
It looks like OfficeRibbonTab manages its own state, which is fine, but it never informs its parent component of the state change. The most common approach would be to supply a callback function to each child component, so that it can then communicate back to the parent:
For example, OfficeRibbon will now contain a function handleTabSelect that gets passed down as a prop to each OfficeRibbonTab. And in OfficeRibbonTab, when a tab is clicked, you simply invoke the callback, and pass in the selected tab's index or id:
OfficeRibbonTab.jsx
setActive(tabIndex) {
this.props.handleTabSelect(tabIndex);
}
OfficeRibbon.jsx
handleTabSelect(tabIndex) {
this.setState({activeIndex: tabIndex});
}
Now in OfficeRibbon, you update your state to set the activeIndex or activeId, again either by the index or an identifier of your choosing. By setting state in OfficeRibbon, we necessarily force a render() of its children. As a result, we simply match the index of the iterator with the activeIndex of your state, when we iterate in render():
<OfficeRibbonTab active={index === this.state.activeIndex} onClick={this.handleTabSelect} ... />

Resources