Vis.js/React/JavaScript: Render Vis.timeline - javascript

I am in the middle of trying to solve a problem with vis.js timeline I hope to get some guidance from you folks. console.log is showing data but the browser shows a blank screen. Unfortunately I am all out of ideas on what else to try it to make it work.
I have the following code. I have tried different ways to make it work but so far no luck. Any help will be greatly appreciated.
// Config for the Timeline as JSON
const options = {
width: '100%',
height: '60px',
stack: false,
showMajorLabels: true,
showCurrentTime: true,
zoomMin: 1000000,
type: 'background',
format: {
minorLabels: {
minute: 'h:mma',
hour: 'ha'
}
}
}
class ScaleTime extends Component{
constructor({data=[]}) {
super({data})
this.state = {data, id:''}
//console.log('ScaleTime Data:', data)
}
render(){
const { data } = this.state
const newAction = data.action.map((actionItem, index) => ({
...actionItem,
id: index + 1
}));
const items = {
...data,
action: newAction
};
const timeLineData = new vis.DataSet([{items}])
console.log('timeLineData:', timeLineData)
var container = document.getElementById('timeline');
return(
<div className="timeline">
<Timeline
items={items.action}
options={options}
container={container}
/>;
</div>
)
}
}//component
Update:
After adding id now I need to change the 'timestamp' property to start. The error message I am now getting is: Property "start" missing in item 1.

you need to make sure that items has content before calling Timeline. You can do:
if (!items) return <SpinLoader />; return <Timeline items={items.action} options={options} container={container} />;

Related

How do I sustain data from DB while using another GET request with different query string in React(Next.js)?

I don't speak English very well. Please be understanding!
First, please check my code!
export default function DriveFolder() {
const [clickFolderPk, setClickFolderPk] = useState(1);
const viewFolder = async () => {
const url = `/api/store/drive/view-folder?folderId=${clickFolderPk}`;
await get(url)
.then((res) => {
console.log(res);
setMainFolder(res.directChildrenFolders);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
viewFolder();
}, [clickFolderPk]);
return (
<div className={classes.driveFolder}>
{mainFolder.map((main, key) => (
<TreeView>
<TreeItem
onClick={() => setClickFolderPk(main.FOLDER_PK)}>
<TreeItem nodeId='10' label='OSS' />
<TreeItem nodeId='6' label='Material-UI'>
<TreeItem nodeId='7' label='src'>
<TreeItem nodeId='8' label='index.js' />
<TreeItem nodeId='9' label='tree-view.js' />
</TreeItem>
</TreeItem>
</TreeItem>
</TreeView>
))}
</div>
);
}
I edited some code to make it clear. (might misspelled)
With this code, on the first rendering, since 'clickFolderPk' value is 1, I get the right data from DB.
However, since I have subfolders within folders from 'clickFolderPk' value 1, I have to request another GET REQUEST to see my subfolders from root folders.
Here is the simple image that you can understand my situation better.
this is what I get from 'clickFolderPk' value 1.
However, when I press 'kikiki', GET request functions and render like this.
.
This is not the way I want to render things.
I want every data from DB, however they don't disappear whenever I use different GET request with different PK number.
I want them stay on the screen and get the subfolders within them.
I'm struggling with this issue for quite a time.
Your help will be really appreciated!!!!!
It's all about Nesting: Folders have sub-folders, etc and it goes on...
Note: To break things down, I will answer from a React point of view disregarding how your backend api is structured or returns data.
Basically there are two main approaches,
Approach #1:
The global state is a single source of truth for all the folders think of it like this:
const [allFolders, setAllFolders] = useState([
{
id: "1",
name: "a-1",
folders: [
{
name: "a-subfolder-1",
folders: [{ name: "a-subfolder-subfolder-1" }],
},
{ name: "subfolder-2" },
],
},
]);
The problem is that any small update requires to mutate the entire state. So I will focus more on Approach #2
Approach #2:
There is the main tree that has child components, child components can expand and have children too:
import { useEffect, useState } from "react";
import "./styles.css";
export default function DriveFolder() {
const [folders, setFolders] = useState([
{ id: "1", name: "folder-a" },
{ id: "2", name: "folder-b" },
{ id: "3", name: "folder-c" }
]);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{folders.map((folder) => {
return <Folder key={folder.id} folder={folder} />;
})}
</div>
);
}
const Folder = ({ parent = undefined, folder }) => {
const [subfolders, setSubfolders] = useState([]);
const [isOpened, setOpened] = useState(false);
const hasSubfolders = subfolders.length > 0;
useEffect(() => {
// send request to your backend to fetch sub-folders
// --------------- to ease stuff I will hard code it
// with this you can limit the example of nest you wish
const maxNestsCount = 5;
const subfolderParent = parent || folder;
const subfolder = {
id: subfolderParent.id + "-sub",
name: "subfolder-of-" + subfolderParent.name
};
const currentNestCount = subfolder.name.split("-sub").length;
setSubfolders(currentNestCount < maxNestsCount ? [subfolder] : []);
// -----------------------------
}, []);
const handleToggleShowSubFolders = (e) => {
e.stopPropagation();
if (!hasSubfolders) {
return;
}
setOpened(!isOpened);
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
paddingHorizontal: 5,
marginTop: 10,
marginLeft: parent ? 20 : 0,
backgroundColor: "#1678F230",
cursor: hasSubfolders ? "pointer" : undefined
}}
onClick={handleToggleShowSubFolders}
>
{folder.name}
<div style={{ display: isOpened ? "block" : "none" }}>
{subfolders.map((subfolder) => (
<Folder key={subfolder.id} parent={folder} folder={subfolder} />
))}
</div>
</div>
);
};
Try it out:
Here is the output of the sample code above:

React component loads data twice on moving back-and-forth between tabs

For some reason my React component seems to remember its old state when going to another tab and back again, instead of reloading completely.
Basically when I click on the "Create" tab in my navbar and back to the "Board" tab data is populated twice instead of once, see image below. When going back the Board component this.state has two of each taskIds, as if it the component state still had the data from the initial page load when loading again. I have a React component looking like this:
const columnOrder = ['todo', 'in-progress', 'in-review', 'done']
const EMPTY_COLUMNS = {
'todo': {
id: 'todo',
title: 'TODO',
taskIds: []
},
'in-progress': {
id: 'in-progress',
title: 'In Progress',
taskIds: [],
},
'in-review': {
id: 'in-review',
title: 'In Review',
taskIds: []
},
'done': {
id: 'done',
title: 'Done',
taskIds: []
}
};
export class Board extends Component {
constructor(props) {
super(props);
this.onLoadEpic = this.onLoadEpic.bind(this);
this.state = {
columnOrder: columnOrder,
columns: {
'in-progress': {
id: 'in-progress',
title: 'In Progress',
taskIds: [],
},
// ...more columns similar to above
},
};
// Load state data on mount
componentDidMount() {
loadEpic(arg1, arg2);
}
// Async function loading items from DB and formatting into useful columns
async loadEpic(arg1, arg2) {
axios.get(...)
.then((response) => {
let data = response.data;
let newTasks = {};
let newColumns = EMPTY_COLUMNS;
data.taskItems.forEach(function(item) {
let id = item.id.toString();
newColumns[item.status]["taskIds"].push(id);
newTasks[id] = {
...item,
id: id
}
});
this.setState({
tasks: newTasks,
columns: newColumns
});
})
}
render() {
// Prints ["7"] on initial load and ["7", "7"] after going back and forth
console.log(this.state.columns["in-progress"].taskIds);
return (
// Simplified, but this is the main idea
<Container>
<DragDropContext onDragEnd={this.onDragEnd}>
{
this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId]
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]
return (
<Column key={column.id} column={column} tasks={tasks}/>
)
}
}
</DragDropContext>
</Container>
)
}
}
and an App.js with Routing looking like this:
export default class App extends Component {
static displayName = App.name;
render () {
return (
<Layout>
<Route exact path='/' component={Board} />
<Route exact path='/create' component={Create} />
</Layout>
);
}
}
Okay, so I figured it out: it's the EMPTY_COLUMNS constant that is bugging out. When the component is re-rendered, the same EMPTY_COLUMNS object is referenced - so the constant is being appended to. Instead, I should make a copy of the empty columns:
// Before - same object is being appended to, doesn't work
let newColumns = EMPTY_COLUMNS;
// After - create a deep copy of the constant, does work
let newColumns = JSON.parse(JSON.stringify(EMPTY_COLUMNS));

ReactJs: TypeError: Cannot read property 'ItemsServices' of undefined

Here I am getting some problems with AliceCarousel to map my response to display its images in the gallery.
I wanted to display the respective types of images for each gallery.
I am generally following SO example .
Any help or suggestion here to make it possible?
Thanks is advance.
//Js
class KitchenService extends Component {
constructor(props) {
super(props);
this.state = {
currentIndex: 0,
responsive: { 1024: { items: 3 } },
galleryItems: this.galleryItems(),
services : this.props.resume,
...props,
ItemsServices:[]
}
}
static propTypes = {
getService: PropTypes.func.isRequired,
resume: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
loading: PropTypes.object.isRequired
}
UNSAFE_componentWillReceiveProps(nextProps) {
if(nextProps.resume !== this.props.resume){
var services = this.props.resume.services;
this.setState({
ItemsServices: services
})
}
}
componentDidMount() {
this.props.getService();
}
slideTo = (i) => this.setState({ currentIndex: i })
onSlideChanged = (e) => this.setState({ currentIndex: e.item })
galleryItems = () => {
return this.state.ItemsServices.map((brand, i) => {
var checkImage = brand.length !== 0 && brand.service_name === "Office";
console.log(checkImage, "checkImage")
return (
<div key={`key-${i}`} className="card-img-top"><img src={brand.service_image_url} /></div>
)
})
};
render() {
const { responsive, currentIndex } = this.state
const items = this.galleryItems();
return(
<div>
<Grid className ="col-12 service-kitchen-gallery-grid" >
<div className="service-gallery-headline">
Kitchen
</div>
<AliceCarousel
dotsDisabled={true}
buttonsDisabled={true}
items={items}
responsive={responsive}
slideToIndex={currentIndex}
onSlideChanged={this.onSlideChanged}
/>
</Grid>
</div>
)
}
}
const mapStateToProps = (state) => ({
resume: state.resume,
});
export default connect(mapStateToProps, {getService }) (KitchenService);
//Error
TypeError: Cannot read property 'ItemsServices' of undefined
service API response
(console.log(services))
[
{
_id: "5f1971da18ba2b04704d65c2",
service_name: "Other",
service_image_url:
"https://res.cloudinary.com/tammycloudinary/image/upload/v1595503076/nou0knjbtkujxwjktang.png",
date: "2020-07-23T11:17:46.928Z",
__v: 0,
},
{
_id: "5f1971b218ba2b04704d65c1",
service_name: "Bedroom",
service_image_url:
"https://res.cloudinary.com/tammycloudinary/image/upload/v1595503036/kfiteeilh4doytio6gs8.png",
date: "2020-07-23T11:17:06.742Z",
__v: 0,
}
];
The issue is not coming from const items = this.galleryItems(); like I originally thought. It is coming from the constructor.
You are attempting to use the state object in order to build the initial state object. This obviously will not work.
constructor(props) {
super(props);
this.state = {
currentIndex: 0,
responsive: { 1024: { items: 3 } },
galleryItems: this.galleryItems(), // <-- Here is the problem
services : this.props.resume,
...props,
ItemsServices:[]
}
}
You attempt to initialize state by calling this.galleryItems. But that function relies on this.state already being declared. Since it has not been created yet (but is in the process of being declared), it is undefined and you get this error.
I don't think gallaryItems really belongs in state at all. It's generally not recommended to store JSX in state anyway. Instead just use the function like you have in the render to compute the JSX needed each render.
Another note: Don't use this.props in the constructor. Instead use the props that are passed in to the constructor.
Y0u can solve this with this solution as well with filter.
render() {
const { services, loading} = this.props.resume;
var checkImage = services.length === 0 ? [] : services.filter((item) => item.service_name === "Kitchen")
return(
<div>
<OwlCarousel className="owl-theme" loop margin={10} nav>
{checkImage.map((item, i) => (
<div className="col-xs-12 item" key={item._id} data-id={item._id} >
<img className="service-gallery-images" src={item.service_image_url} alt=""/>
</div>
))}
</OwlCarousel>
</div>
)
}

Label text not updating in MUIDataTable ReactJS

I want to add multi language option in mui Datatables. I can change the translations but when I want to change language, I tried to give another object with the other translations (this object if I do console log I can see the changes) but the label texts not change.
I used a contextProvider to change the language selected and then get the specific dictionary with the translations.
Is a class component, so I did a static contextType with the correct provider.
Is there any possibility to re-render the element with another options or something like that?
options = {
textLabels: this.context.translation.dataTables.textLabels
};
return(
<MUIDataTable
title={this.context.language.value}
data={data}
columns={columns}
options={options}
/>
);
The best approach to re-render Mui-Datatables its updating the key of the table
key={this.context.language.value}
<MUIDataTable
key={this.context.language.value}
title={this.context.language.value}
data={data}
columns={columns}
options={options}
/>
You can force React component rendering:
There are multiple ways to force a React component rendering but they are essentially the same. The first is using this.forceUpdate(), which skips shouldComponentUpdate:
someMethod() {
// Force rendering without state change...
this.forceUpdate();
}
Assuming your component has a state, you could also call the following:
someMethod() {
// Force rendering with a simulated state change
this.setState({ state: this.state });
}
use customRowRender Function in the options and manipulate table with respect to language
Override default row rendering with custom function.
customRowRender(data, dataIndex, rowIndex) => React Component
In MUIDataTable, We can override label name by providing label in MUIDataTableColumnDef options while making column.
Example :
const columns: MUIDataTableColumnDef[] = [
{
name: 'Id',
label: 'ID',
options: {
download: false,
customBodyRenderLite: (index: number) => {
const desc: Description = evenMoreAbout[index]
return <BasicInfo obj={desc} setIconClicked={setIconClicked} />
}
}
},
{
name: 'id',
label: 'ID',
options: {
display: 'excluded',
download: true,
customBodyRender: desc => desc.id
}
}]
Even though if we still want to over ride the label name on some condition of data using customHeadLabelRender ... we can as like below example
const columns: MUIDataTableColumnDef[] = [
{
name: 'Id',
label: '',
options: {
download: false,
customBodyRenderLite: (index: number) => {
const desc: Description = evenMoreAbout[index]
return <BasicInfo obj={desc} setIconClicked={setIconClicked} />
},
customHeadLabelRender: (dataIndex: number, rowIndex: number) => {
return 'ID';
}
}
}
]

React-Native Updating List View DataSource

I have an iOS app I am making with react-native. The Game class contains a ListView component. I set the state in the constructor and include a dataSource. I have a hardcoded array of data for right now that I store in a different state property (this.state.ds). Then in the componentDidMount I use the cloneWithRows method to clone my this.state.ds as my dataSource for the view. That is pretty standard as far as ListViews go and is working well. Here is the code:
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
'use strict';
var React = require("react-native");
var { StyleSheet, Text, View, ListView, TouchableHighlight } = React;
class Games extends React.Component {
constructor(props) {
super(props);
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 != r2
});
this.state = {
ds: [
{ AwayTeam: "TeamA", HomeTeam: "TeamB", Selection: "AwayTeam" },
{ AwayTeam: "TeamC", HomeTeam: "TeamD", Selection: "HomeTeam" }
],
dataSource: ds
};
}
componentDidMount() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.state.ds)
});
}
pressRow(rowData) {
var newDs = [];
newDs = this.state.ds;
newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newDs)
});
}
renderRow(rowData) {
return (
<TouchableHighlight
onPress={() => this.pressRow(rowData)}
underlayColor="#ddd"
>
<View style={styles.row}>
<Text style={{ fontSize: 18 }}>
{rowData.AwayTeam} # {rowData.HomeTeam}{" "}
</Text>
<View style={{ flex: 1 }}>
<Text style={styles.selectionText}>
{rowData[rowData.Selection]}
</Text>
</View>
</View>
</TouchableHighlight>
);
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
/>
);
}
}
var styles = StyleSheet.create({
row: {
flex: 1,
flexDirection: "row",
padding: 18,
borderBottomWidth: 1,
borderColor: "#d7d7d7"
},
selectionText: {
fontSize: 15,
paddingTop: 3,
color: "#b5b5b5",
textAlign: "right"
}
});
module.exports = Games
The issue I am having comes in the pressRow method. When the user presses the row, I would like the selection to change and for it to render the change on the device. Through some debugging, I have noticed that even though I am changing the Selection property of the object in the newDs array, the same property changes on the object in this.state.ds and similarly changes the object in this.state.dataSource._dataBlob.s1. Through further debugging, I have found that since those other arrays have changed, the ListView's DataSource object doesn't recognize the change because when I set the state and rowHasChanged is called, the array it is cloning matches the array this.state.dataSource._dataBlob.s1 and so it doesn't look like a change and doesn't rerender.
Any ideas?
Try this:
pressRow(rowData){
var newDs = [];
newDs = this.state.ds.slice();
newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newDs)
})
}
This should make a copy of the array, which can then be modified independently of the original array in this.state.ds.
In case anyone comes across this issue like I did, here's the complete solution.
Basically, there seems to be a bug with the ListView component and you need to rebuild each item that changes in the datasource for the ListView to redraw it.
First, create the datasource and a copy of the array and save them to state. In my case, foods is the array.
constructor(props){
super(props);
var ds = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.state = {
dataSource: ds.cloneWithRows(foods),
db: foods,
};
}
When you want to change something in the datasource, make a copy of the array you saved to the state, rebuild the item with the change and then save the new array with the change back to the state (both db and datasource).
onCollapse(rowID: number) {
console.log("rowID", rowID);
var newArray = this.state.db.slice();
newArray[rowID] = {
key: newArray[rowID].key,
details: newArray[rowID].details,
isCollapsed: newArray[rowID].isCollapsed == false ? true : false,
};
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newArray),
db: newArray,
});
}
react is smart enough to detect changes in dataSource and if the list should be re-rendered. If you want to update listView, create new objects instead of updating the properties of existing objects.
The code would look something like this:
let newArray = this._rows.slice();
newArray[rowID] = {
...this._rows[rowID],
Selection: !this._rows[rowID].Selection,
};
this._rows = newArray;
let newDataSource = this.ds.cloneWithRows(newArray);
this.setState({
dataSource: newDataSource
});
You can read more about similar issue on Github

Categories