Unable to find node on an unmounted component (react-sortable-hoc) - react-native

A have small gutenberg block, but i have errors.
Unable to find node on an unmounted component
undefined is not an object (evaluating 'this.refs[collection].indexOf')
Current wp version uses React 16.13.1
How can i fix this? Thanks!
import {SortableContainer, SortableElement} from 'react-sortable-hoc';
registerBlockType('ay/sortable', {
title: __('Sortable'),
attributes: {
items: {
type: 'array',
default: [
'Item 1',
'Item 2',
'Item 3',
],
},
},
edit(props) {
const {
attributes: {items},
setAttributes,
} = props;
const SortableItem = SortableElement(({value}) => <li>{value}</li>);
const SortableList = SortableContainer(({items}) => {
return (
<ul>
{items.map((value, index) => (
<SortableItem
key={`item-${value}`}
index={index}
value={value}
/>
))}
</ul>
);
});
const onSortEnd = ({oldIndex, newIndex}) => {
setAttributes(({items}) => ({
items: arrayMove(items, oldIndex, newIndex),
}));
};
return (
<div>
<SortableList items={items} onSortEnd={onSortEnd} />
</div>
);
},
save(props) {
return null;
},
});

In your code provided, there are two critical imports missing for your block to work, add:
...
import { registerBlockType } from '#wordpress/blocks';
import { __ } from '#wordpress/i18n';
...
Also check that your package.json lists react-sortable-hoc as a dependancy:
...
"devDependencies": {
...
react-sortable-hoc": "^2.0.0"
},
After adding the missing imports and making sure the dependancy is present/installed, your block should compile without error. I tested your code all the other parts of the edit() function are fine and the output was a sortable list.

Related

How to hide button after pressing in material-table

I am using react material-table. I am in need of a feature like this: I use remote data mode to get a list of records, then I use the custom column rendering function to add a Material Button at the end of each row in the table, When the user presses this button I want it to be hidden. How can I do that. I look forward to receiving your help.
This is the illustration image
I made this example, on button click it gets disabled and a variable is set to to a loading state:
The key aspect here is to define something that identifies the row that is being updated. I use an extra column on which you could also display a spinner component:
{
field: "isUpdating",
render: (rowdata) =>
fetchingClient === rowdata.name
? "loading.." // Add your <Spinner />
: null
},
Since you want to render the button as a custom column (other way could be using actions), on the render attribute of that column, you can use rowdata parameter to access what you are looking for:
{
field: "join",
sorting: false,
render: (rowdata) => (
<button
disabled={fetchingClient === rowdata.name}
onClick={(event) => fetchDataFromRemote(rowdata.name)}
>
Go fetch
</button>
)
}
Here is the link to the sandbox and the complete code, I hope this works for you!
import React, { Fragment, useState } from "react";
import MaterialTable from "material-table";
export default function CustomEditComponent(props) {
const [fetchingClient, setFetchingClient] = useState("");
const fetchDataFromRemote = (clientName) => {
console.log(clientName);
setFetchingClient(clientName);
};
const tableColumns = [
{ title: "Client", field: "client" },
{ title: "Name", field: "name" },
{
field: "isUpdating",
render: (rowdata) =>
fetchingClient === rowdata.name
? "loading.." // Add your <Spinner />
: null,
},
{
field: "join",
sorting: false,
render: (rowdata) => (
<button
disabled={fetchingClient === rowdata.name}
onClick={(event) => fetchDataFromRemote(rowdata.name)}
>
Go fetch
</button>
),
},
];
const tableData = [
{
client: "client1",
name: "Jasnah",
year: "2019",
},
{
client: "client2",
name: "Dalinar",
year: "2018",
},
{
client: "client3",
name: "Kal",
year: "2019",
},
];
return (
<Fragment>
<MaterialTable
columns={tableColumns}
data={tableData}
title="Material Table - custom column "
options={{ search: false }}
/>
</Fragment>
);
}

The array for multi select drop down is not referenced well and gives an error

I am trying to use a multi select drop down as explained here but one way or the other the items array is not properly defined.
This is my code for the friendselector component:
/* eslint-disable react-native/no-inline-styles */
import React, {Component} from 'react';
import {View} from 'react-native';
import MultiSelect from 'react-native-multiple-select';
export default class FriendSelector extends Component {
constructor() {
super();
this.state = {
selectedItems: [],
items: [
{
id: '92iijs7yta',
name: 'Kenneth',
},
{
id: 'a0s0a8ssbsd',
name: 'Ann',
},
{
id: '16hbajsabsd',
name: 'Leen',
},
{
id: 'nahs75a5sg',
name: 'Kris',
},
{
id: '667atsas',
name: 'Steve',
},
{
id: 'suudydjsjd',
name: 'Sarah',
},
],
};
}
onSelectedItemsChange = selectedItems => {
this.setState({selectedItems});
};
render() {
const {selectedItems} = this.state;
return (
<View>
<MultiSelect
hideTags
items={this.items}
uniqueKey="id"
ref={component => {
this.multiSelect = component;
}}
onSelectedItemsChange={this.onSelectedItemsChange}
selectedItems={selectedItems}
selectText="Pick friend(s)"
searchInputPlaceholderText="Search..."
onChangeInput={text => console.log(text)}
tagRemoveIconColor="#CCC"
tagBorderColor="#CCC"
tagTextColor="#CCC"
selectedItemTextColor="#CCC"
selectedItemIconColor="#CCC"
itemTextColor="#000"
displayKey="name"
searchInputStyle={{color: '#CCC'}}
submitButtonColor="#CCC"
submitButtonText="Submit"
/>
<View>
{this.multiSelect &&
this.multiSelect.getSelectedItemsExt(selectedItems)}
</View>
</View>
);
}
}
which is similar as in this tutorial, I did change some references to items with state - this to connect things to one another. Still the items are not loaded in items={this.items} I have the feeling.
Does anyone know why?
This is the error:
Thanks for your answer!
items prop is getting an undefined value because it's assigned this.items which doesn't exist in your class.
Change items={this.items} to items={this.sate.items} it makes more sense.
Another solution is to declare items array outside of the state.

How can I make an entire row clickable in a DetailsList component? (office-ui-fabric)

I've been using the DetailsList component but haven't found a way to make an entire row clickable - is there a sample code snippet or pointers on how this can be achieved?
https://developer.microsoft.com/en-us/fabric#/components/detailslist
Overriding onRenderRow worked for me.
const _columns: IColumn[] = [
{
key: 'name',
name: 'Name',
fieldName: 'name',
minWidth: 100,
maxWidth: 200,
isResizable: true
},
{
key: 'value',
name: 'Value',
fieldName: 'value',
minWidth: 100,
maxWidth: 200,
isResizable: true,
}
];
const _items = Array.apply(null, Array(10)).map((_: any, num: number) => ({
key: num.toString(),
name: `Item ${num.toString()}`,
value: num.toString()
}));
class Content extends React.Component {
private _renderRow(props: Fabric.IDetailsRowProps, defaultRender: any): JSX.Element {
return (
<Fabric.DetailsRow {...props} onClick={() => alert('click')}/>
);
}
public render() {
return (
<Fabric.Fabric>
<Fabric.DetailsList
items={ _items }
columns={ _columns.concat(..._columns, ..._columns, ..._columns) }
onRenderRow={this._renderRow}
/>
</Fabric.Fabric>
);
}
}
ReactDOM.render(
<Content />,
document.getElementById('content')
);
Here is th e single click solution,
Use onActiveItemChanged prop like:
const _onActiveItemChanged = (item): void => {
alert(`Item invoked: ${JSON.stringify(item)}`);
};
here is the DetailList
<DetailsList
items={assessmentList}
compact={false}
columns={columns}
onActiveItemChanged={_onActiveItemChanged }
/>
Please onItemInvoked Property and method. But it works on double click. I am also looking for single click solution
const onItemInvoked = (item): void => {
alert(`Item invoked: ${JSON.stringify(item)}`);
};
<DetailsList
items={assessmentList}
compact={false}
columns={columns}
selectionMode={SelectionMode.multiple}
getKey={_getKey}
setKey="multiple"
layoutMode={DetailsListLayoutMode.justified}
checkboxVisibility={CheckboxVisibility.hidden}
isHeaderVisible={true}
selectionPreservedOnEmptyClick={false}
enterModalSelectionOnTouch={true}
ariaLabelForSelectionColumn="Toggle selection"
ariaLabelForSelectAllCheckbox="Toggle selection for all items"
checkButtonAriaLabel="Select Checkbox"
onRenderRow={onRenderRow}
onItemInvoked={onItemInvoked}
onRenderDetailsHeader={(headerProps, defaultRender) => {
return (
<Sticky
stickyPosition={StickyPositionType.Header}
isScrollSynced={true}
stickyBackgroundColor="transparent"
>
<div className="text-center">{defaultRender(headerProps)}</div>
</Sticky>
);
}}
/>
Use onRenderRow and cloneElement to attach an onClick listener:
import React, { useCallback, cloneElement } from 'react';
import { DetailsList } from '#fluentui/react';
const Component = () => {
const onItemInvoked = useCallback( ( item ) => {
console.log( item );
}, [] );
const onRenderRow = useCallback( ( row, defaultRender ) => {
return cloneElement( defaultRender( row ), { onClick: () => onItemInvoked( row.item ) } )
}, [ onItemInvoked ] );
return (
<DetailsList
onRenderRow={ onRenderRow }
/>
);
};

How to make dynamic checkbox in react native

I am making a react native application in which i need to make checkbox during runtime.I means that from server i will get the json object which will have id and label for checkbox.Now i want to know that after fetching data from server how can i make checkbox also how can i handle the checkbox , i mean that how many number of checkbox will be there it will not be static so how can i declare state variables which can handle the checkbox.Also how can i handle the onPress event of checkbox.Please provide me some help of code .Thanks in advance
The concept will be using an array in the state and setting the state array with the data you got from the service response, Checkbox is not available in both platforms so you will have to use react-native-elements. And you can use the map function to render the checkboxes from the array, and have an onPress to change the state accordingly. The code will be as below. You will have to think about maintaining the checked value in the state as well.
import React, { Component } from 'react';
import { View } from 'react-native';
import { CheckBox } from 'react-native-elements';
export default class Sample extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{ id: 1, key: 'test1', checked: false },
{ id: 2, key: 'test1', checked: true }
]
};
}
onCheckChanged(id) {
const data = this.state.data;
const index = data.findIndex(x => x.id === id);
data[index].checked = !data[index].checked;
this.setState(data);
}
render() {
return (<View>
{
this.state.data.map((item,key) => <CheckBox title={item.key} key={key} checked={item.checked} onPress={()=>this.onCheckChanged(item.id)}/>)
}
</View>)
}
}
Here's an example how you can do this. You can play with the code, to understand more how it's working.
export default class App extends React.Component {
state = {
checkboxes: [],
};
async componentDidMount() {
// mocking a datafetch
setTimeout(() => {
// mock data
const data = [{ id: 1, label: 'first' }, { id: 2, label: 'second' }];
this.setState({
checkboxes: data.map(x => {
x['value'] = false;
return x;
}),
});
}, 1000);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
{JSON.stringify(this.state)}
</Text>
{this.state.checkboxes.length > 0 &&
this.state.checkboxes.map(checkbox => (
<View>
<Text>{checkbox.label}</Text>
<CheckBox
onValueChange={value =>
this.setState(state => {
const index = state.checkboxes.findIndex(
x => x.id === checkbox.id
);
return {
checkboxes: [
...state.checkboxes.slice(0, index),
{ id: checkbox.id, label: checkbox.label, value },
...state.checkboxes.slice(index+1),
],
};
})
}
value={checkbox.value}
key={checkbox.id}
/>
</View>
))}
</View>
);
}
}

How to test that the renderItem function returns a <ListItem />?

I'm building my app with React Native and I do my unit tests with Jest and Enzyme. How can I test my <FlatList />'s renderItem() function?
It returns a <ListItem /> from the React-Native-Elements library.
Let me give you the example code:
import { ListItem } from 'react-native-elements'
export class MyList extends Component {
const list = [
{
name: 'Amy Farha',
subtitle: 'Vice President'
},
{
name: 'Chris Jackson',
avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg',
subtitle: 'Vice Chairman'
},
... // more items
]
keyExtractor = (item, index) => index
renderItem = ({ item }) => (
<ListItem
title={item.name}
subtitle={item.subtitle}
leftAvatar={{
source: item.avatar_url && { uri: item.avatar_url },
title: item.name[0]
}}
/>
)
render () {
return (
<FlatList
keyExtractor={this.keyExtractor}
data={this.state.dataSource}
renderItem={this.renderItem}
/>
)
}
}
I would like to be able to test the renderItem() function. My problem is, that wrapper.instance().renderItem({item: item}) returns the error: TypeError: wrapper.instance(...).renderItem(...).find is not a function. Let me give you the code of the test that I wrote:
describe("component methods", () => {
let wrapper;
let props;
let item;
beforeEach(() => {
props = createTestProps();
wrapper = shallow(<MyList {...props} />);
});
describe("renderItem", () => {
describe("user", () => {
beforeEach(() => {
item = {
name: 'Chris Jackson',
avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg',
subtitle: 'Vice Chairman'
};
});
it("should display the order as a <ListItem />", () => {
expect(
wrapper
.instance()
.renderItem(item)
.find("ListItem")
).toHaveLength(1);
});
});
});
});
How would I have to write this test so that I can test if the function correctly renders a <ListItem />?
You can test FlatList with react-native-testing-library
Here's example:
Components:
const Item = ({ name ) => <Text>{name}</Text>
class LisItem extends React.Component {
_keyExtractor = (item: { id: string }) => item.id
render() {
return (
<View style={styles.container}>
{todos && (
<FlatList
data={this.props.todos}
keyExtractor={this._keyExtractor}
renderItem={({ item: { id, name } }) => (
<Item
key={id}
name={name}
/>
)}
/>
)}
</View>
)
}
}
Unit testing:
import { render } from 'react-native-testing-library'
const mockDataTodos = [
{
id: 'id-1',
name: 'Todo-1',
},
{
id: 'id-2',
name: 'Todo-2',
},
{
id: 'id-3',
name: 'Todo-3',
},
]
describe('Testing FlatList', () => {
test('Error component should be render when error is true', () => {
const componentTree = render(
<ListItem todos={mockDataTodos} />,
)
expect(componentTree.getAllByType(FlatList).length).toBe(1)
expect(componentTree.getAllByType(Item).length).toBe(mockDataTodos.length)
})
})
Hope this help!
renderItem() returns a JSX element. JSX compiles to React.createElement() which returns an object.
Therefore, the return value from renderItem() is just an object.
You can test that renderItem() creates the correct object by doing the following:
it("should display the order as a <ListItem />", () => {
const element = wrapper
.instance()
.renderItem(item);
expect(element.type).toBe(ListItem);
expect(element.props).toEqual({
title: 'Chris Jackson',
subtitle: 'Vice Chairman',
leftAvatar: {
source: { uri: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg' },
title: 'C'
}
});
});
You can also test using the renderProp function.
const wrapper = shallow(<YourComponent {...defaultProps} />);
const flatList = wrapper.find('FlatList');
const item = flatList.renderProp('renderItem')({ item: yourData });
expect(item).toMatchSnapshot();
So I'm not sure whether you've extracted some code out of a class-based component, but renderItem itself is a component. I can maybe give you some test code for that and you can adapt it for your needs, assuming you imported shallow and set up your item variable:
describe('renderItem', () => {
it('should display as a ListItem', () => {
const wrapper = shallow(<renderItem item={item} />);
expect(wrapper.find(ListItem).length).toEqual(1);
});
});
There are two key things that are different from your example. One - I assume here you've imported ListItem into your test. You can then pass that directly into find. The second bit is you want to pass the result of finding and checking the length into expect and test the value of that. Think of it is "what I want to test" (the number of ListItem components it can find) and then off of that you create your assertion (toEqual(1)).
Additional Information to Reflect Question Edit
In your setup, I wouldn't bother to test renderItem directly. Instead I'd ensure full testing of ListItem, and then assert things about how MyList renders FlatList. This can be done using expect(wrapper).toMatchSnapshot(), or even better by asserting some things about the props given to FlatList. If you are really paranoid about all of this, perhaps use mount instead of shallow to render it completely.