react-sortable-tree - How to get the search API working - javascript

According to the API doc there needs to be a searchQuery prop which i've mentioned in my code but the search doesn't seem to be working
API doc doesn't explain how to implement it and the examples available online don't seem to be working on code sandbox.
The only article available which seems to explain search has incorrect code (duplicate props): https://frugalisminds.com/how-to-create-react-sortable-tree/
API Doc: https://www.npmjs.com/package/react-sortable-tree
Below is the code:
import React, { Component } from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css";
export default class Tree extends Component {
constructor(props) {
super(props);
this.state = {
treeData: [
{ title: "Chicken", children: [{ title: "Egg" }] },
{ title: "Fish", children: [{ title: "fingerline" }] },
],
searchString: ""
};
}
handleSearchOnChange = e => {
this.setState({
searchString: e.target.value,
});
};
render() {
return (
<div style={{ height: 400 }}>
<input
type="search"
onChange={this.handleSearchOnChange}
className="form-control"
/>
<SortableTree
searchQuery={this.state.searchString}
treeData={this.state.treeData}
onChange={treeData => this.setState([...treeData])}
isVirtualized={false}
/>
</div>
);
}
}

missing a searchFocusOffset to highlight the found item and a searchMethod which can be custom defined inside render method as follows:
import React, { Component } from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css"; // This only needs to be imported once in your app
export default class Tree extends Component {
constructor(props) {
super(props);
this.state = {
treeData: [
{ title: "Chicken", children: [{ title: "Egg" }] },
{ title: "Fish", children: [{ title: "fingerline" }] },
],
searchString: ""
};
}
render() {
// Case insensitive search of `node.title`
const customSearchMethod = ({ node, searchQuery }) =>
searchQuery &&
node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1;
return (
<div style={{ height: 400 }}>
<input
type="search"
onChange={event => this.setState({ searchString: event.target.value })}
className="form-control"
/>
<SortableTree
searchMethod={customSearchMethod}
searchQuery={this.state.searchString}
searchFocusOffset={0}
treeData={this.state.treeData}
onChange={treeData => this.setState([...treeData])}
isVirtualized={false}
/>
</div>
);
}
}

Related

How can I fix setState() so that it works?

I'm following Bob Ziroll's free scrimba course on React.
Thing is, my code is the same with his and it has been working so far...
but it isn't working anymore.
Here's my code
App.js
import React, { Component } from "react";
import TodoItem from "./components/TodoItem";
import todosData from "./components/todosData";
class App extends Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState(prevState => {
console.log("PrevState Start ", prevState.todos )
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
console.log("Changed", id)
}
render() {
const todoItem = this.state.todos.map(x =>
<TodoItem handleChange = {this.handleChange}
key={x.id}
item={x}
/>
)
return (
<div>
{todoItem}
</div>
);
}
}
export default App;
And here's the code for TodoItem.js
import React from 'react'
function TodoItem(props) {
return (
<div>
<input type='checkbox' checked={props.item.completed} onChange={() => props.handleChange(props.item.id)} />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
And here's todosData.js*
const todosData = [
{
id: 1,
text: "Take out the trash",
completed: true
},
{
id: 2,
text: "Grocery shopping",
completed: false
},
{
id: 3,
text: "Clean gecko tank",
completed: false
},
{
id: 4,
text: "Mow Lawn",
completed: true
},
{
id: 5,
text: "Catch up on arrested development",
completed: false
}
]
export default todosData
I've tried using a callback but it isn't working. I've checked prevState and the updated state, but no change is reflected.
I'd appreciate your help on this.
When you are updating the state using setState, You should return new object to tell react that this is the change and React will update it accordingly.
Live Demo
React will compare the reference of two objects and if you won't return new object then react will take as a same object. React won't figure out when you won't return new object. You are just updating a property of an object as :
if (todo.id === id) {
todo.completed = !todo.completed
}
You just have to make a small change as:
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
}
Being super curious, I tried your code and it works perfectly for me. Is this not what you wanted to happen? Check the snippet below:
document.onreadystatechange = () => {
const { useState, Component } = React;
class App extends Component {
constructor() {
super();
this.state = {
todos: todosData,
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id) {
this.setState((prevState) => {
console.log("PrevState Start ", prevState.todos);
const updatedTodos = prevState.todos.map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
return {
todos: updatedTodos,
};
});
console.log("Changed", id);
}
render() {
const todoItem = this.state.todos.map((x) => (
<TodoItem handleChange={this.handleChange} key={x.id} item={x} />
));
return <div>{todoItem}</div>;
}
}
function TodoItem(props) {
return (
<div>
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
);
}
const todosData = [
{
id: 1,
text: "Take out the trash",
completed: true,
},
{
id: 2,
text: "Grocery shopping",
completed: false,
},
{
id: 3,
text: "Clean gecko tank",
completed: false,
},
{
id: 4,
text: "Mow Lawn",
completed: true,
},
{
id: 5,
text: "Catch up on arrested development",
completed: false,
},
];
ReactDOM.render(<App />, document.querySelector("#root"));
};
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>

How can i solve the problem from the book road to react at page 78 ? I keep getting undefine for one of the methods in the table component

import React, { Component } from 'react';
import './App.css';
const list = [
{
title: 'React',
url: 'https://facebook.github.io/react/',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
{
title: 'Redux',
url: 'https://github.com/reactjs/redux',
author: 'Dan Abramov, Andrew Clark',
num_comments: 2,
points: 5,
objectID: 1,
},
];
class App extends Component {
state = {
list,
text: 'abc',
searchTerm: ''
}
onDisMiss = (id) => {
const updateList = this.state.list.filter((item) => item.objectID != id)
return () => this.setState({ list: updateList })
}
onSearchChange = (event) => {
this.setState({ searchTerm: event.target.value })
}
isSearched = (searchTerm) => {
return (item) => item.title.toLowerCase().includes(searchTerm.toLowerCase())
}
render() {
const { searchTerm, list } = this.state
return (
<div>
<Search value={searchTerm}
onChange={this.onSearchChange}>Search</Search>
<Table list={list} pattern={searchTerm} onDissMiss={this.onDisMiss} />
</div>
);
}
}
class Search extends Component {
render() {
const { value, onChange, children } = this.props
return (
<div>
<form>
{children}<input type="text" onChange={onChange} value={value} />
</form>
</div>
);
}
}
class Table extends Component {
render() {
const { list, pattern, onDisMiss } = this.props
return (
<div>
{list.filter(isSearched(pattern)).map(item =>
<div key={item.objectID}>
<span><a href={item.url}>{item.title}</a></span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={onDisMiss(item.objectID)} type="button">Dismiss</button>
</span>
</div>)
}
</div>
);
}
}
export default App;
Road to react Book The Table component related.I get undefined for the isSearched method. how can I fix it so it works correctly its from the book road to react it seems like the book has a few error which I have problems solving because am just learning react. can you help with the solution and why this problem is actually happening
You should put the isSearched method inside the Table class and not the App class

On click of a row get the data of that particular row

I am using react-table for data-grid purposes. I have extracted react-table as a separate component where-in I just pass necessary props to it and it renders the grid.
I am trying to get the info related to a particular row whenever I click on it. I am trying getTrProps but does not seem like working.
Sandbox: https://codesandbox.io/s/react-table-row-table-g3kd5
App Component
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";
interface IProps {}
interface IState {
data: {}[];
columns: {}[];
}
class App extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
data: [],
columns: []
};
}
componentDidMount() {
this.getData();
}
getData = () => {
let data = [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" },
{ firstName: "Pete", status: "Approved", age: "17" }
];
this.setState({ data }, () => this.getColumns());
};
getColumns = () => {
let columns = [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Status",
accessor: "status"
},
{
Header: "Age",
accessor: "age"
}
];
this.setState({ columns });
};
onClickRow = () => {
console.log("test");
};
render() {
return (
<>
<DataGrid
data={this.state.data}
columns={this.state.columns}
rowClicked={this.onClickRow}
/>
</>
);
}
}
render(<App />, document.getElementById("root"));
DataGrid Component
import * as React from "react";
import ReactTable from "react-table";
import "react-table/react-table.css";
interface IProps {
data: any;
columns: any;
rowClicked(): void;
}
interface IState {}
export default class DataGrid extends React.Component<IProps, IState> {
onRowClick = (state: any, rowInfo: any, column: any, instance: any) => {
this.props.rowClicked();
};
render() {
return (
<>
<ReactTable
data={this.props.data}
columns={this.props.columns}
getTdProps={this.onRowClick}
/>
</>
);
}
}
Use this code to get info of a clicked row:
getTdProps={(state, rowInfo, column, instance) => {
return {
onClick: (e, handleOriginal) => {
console.log("row info:", rowInfo);
if (handleOriginal) {
handleOriginal();
}
}
}}}
You can check this CodeSandbox example: https://codesandbox.io/s/react-table-row-table-shehb?fontsize=14
you have quite a few errors in your code but to pass the value back you have to put it into your callback:
onRowClick = (state: any, rowInfo: any, column: any, instance: any) => {
this.props.rowClicked(rowInfo);
};
and read it out like this:
onClickRow = (rowInfo) => {
console.log(rowInfo);
};
Hope this helps.

How do I add input data to JSON array in React.

I've been working on understanding React concepts and did my Todo project. I have the dummy data displaying, but can't add a new value to my dummy data, which is stored in an array of objects in a separate file, todos.js.
Here is the file hierarchy
Here is the error I am getting -
index.js:2177 Warning: Each child in an array or iterator should have a unique "key" prop.
TodoList.js
import React from 'react';
import Todo from './Todo';
import todos from '../todos'
class TodoList extends React.Component {
constructor() {
super();
this.state = {
todoItems: todos,
newItem: {}
}
}
addItem = (event) => {
event.preventDefault();
const todoList = this.state.todoItems;
todoList.push(this.state.newItem);
this.setState({
todoList: todos,
newItem: {}
});
};
handleInput = (event) => {
this.setState({ newItem: event.target.value });
}
render() {
const itenary = this.state.todoItems;
return (
<div>
{itenary.map(todo =>
<div key={todo.id}>
<Todo handleClick={this.props.handleClick} thing={todo} />
</div>
)}
<br />
<form onSubmit={this.addItem}>
<input type="text" onChange={this.handleInput} placeholder="Add a new task" />
<button>Submit</button>
</form>
</div>
);
}
}
export default TodoList;
Todo.js
import React from 'react';
class Todo extends React.Component {
constructor() {
super();
this.state = {
clicked: false
}
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked });
}
render() {
const styles = this.state.clicked ? { textDecoration: 'line-through' } : { textDecoration: 'none' };
return (
{/* This is where the todo item is*/}
<div style={styles} onClick={this.handleClick} key={this.props.thing.id}>{this.props.thing.text}</div>
);
}
}
export default Todo;
todos.js
const todos = [
{ id: 1, text: 'Go to the gym', 'completed': false },
{ id: 2, text: 'Do laundry', 'completed': false },
{ id: 3, text: 'Study for exams', 'completed': false },
{ id: 4, text: 'Read a book', 'completed': false },
{ id: 5, text: 'Clean the bedroom', 'completed': false },
{ id: 6, text: 'Go to the park', 'completed': false },
];
export default todos;
Any help and/or feedback is appreciated.
You must give the new todo you add to todoItems a unique id that React can use to distinguish it from the others when you render them.
You should also not mutate the current state by using push. You should instead set state with an entirely new array that contains everything the previous one did.
Example
class TodoList extends React.Component {
constructor() {
super();
this.state = {
todoItems: todos,
newItem: ""
};
}
addItem = event => {
event.preventDefault();
this.setState(prevState => {
return {
todoItems: [
...prevState.todoItems,
{ id: Math.random(), text: prevState.newItem, completed: false }
],
newItem: ""
};
});
};
// ...
}
const todos = [
{ id: 1, text: "Go to the gym", completed: false },
{ id: 2, text: "Do laundry", completed: false },
{ id: 3, text: "Study for exams", completed: false },
{ id: 4, text: "Read a book", completed: false },
{ id: 5, text: "Clean the bedroom", completed: false },
{ id: 6, text: "Go to the park", completed: false }
];
class TodoList extends React.Component {
constructor() {
super();
this.state = {
todoItems: todos,
newItem: ""
};
}
addItem = event => {
event.preventDefault();
this.setState(prevState => {
return {
todoItems: [
...prevState.todoItems,
{ id: Math.random(), text: prevState.newItem, completed: false }
],
newItem: ""
};
});
};
handleInput = event => {
this.setState({ newItem: event.target.value });
};
render() {
const itenary = this.state.todoItems;
return (
<div>
{itenary.map(todo => (
<div key={todo.id}>
<Todo handleClick={this.props.handleClick} thing={todo} />
</div>
))}
<br />
<form onSubmit={this.addItem}>
<input
type="text"
onChange={this.handleInput}
value={this.state.newItem}
placeholder="Add a new task"
/>
<button>Submit</button>
</form>
</div>
);
}
}
class Todo extends React.Component {
constructor() {
super();
this.state = {
clicked: false
};
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked });
};
render() {
const styles = this.state.clicked
? { textDecoration: "line-through" }
: { textDecoration: "none" };
return (
<div style={styles} onClick={this.handleClick} key={this.props.thing.id}>
{this.props.thing.text}
</div>
);
}
}
ReactDOM.render(<TodoList />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You can always define the Object/s array within a function accepting an array:
In this example:
const [programs, setPrograms] = useState([]);
In order to create a single JSON program array:
setPrograms([{id: program?.id, title: program?.title}]);
How to create Array in JSON.
[ { "id": "1", "text": "Hello", "status": "false" }, { "id": "2", "text": "Coding Techniques", "status": "true" }, ]
Watch this video for more help
https://youtu.be/zgFOIdBIn4w

This.props returning undefined?

I'm currently passing data into my component via props, and for some reason it's showing up as undefined.
From my parent component perspective I have the following pass 2 props, data and header.
class ContractTable extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
render() {
return (
<div>
<p></p>
<MuiThemeProvider>
<TableTest
data={this.state.data}
header={[
{
name: "First Name",
prop: "firstName"
},
{
name: "Last Name",
prop: "lastName"
},
{
name: "Age",
prop: "age"
},
{
name: "Height",
prop: "height"
},
{
name: "Address",
prop: "address"
}
]}
/>
</MuiThemeProvider>
<p></p>
</div>
);
}
I try to grab the props and set it as my state, but when I log this.props.data or this.props.header it returns undefined. Why is this?
import React, { Component } from 'react';
import {
Table,
TableBody,
TableHeader,
TableHeaderColumn,
TableRow,
TableRowColumn,
} from 'material-ui/Table';
class TableTest extends Component {
constructor(props) {
super(props);
this.state = {
data: props.data,
header: props.header
}
this.row = this.row.bind(this);
}
row = (currentValue, index, header) => (
<TableRow key={`t-${index}`}>
{
header.map((headerName, index) => (
<TableRowColumn key={`trc-${index}`}>
{currentValue[headerName.prop]}
</TableRowColumn>
))
}
</TableRow>
);
render() {
return 'hello'
}
}
export default TableTest;
Update: take a look https://jsfiddle.net/nroLmghv/
Just rendered simple table header.
Passing props to state is not a good approach.
I created a snippet. And it looks working. Point in which place do you have a problem. Or provide MuiThemeProvider and TableTest full code.
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
// mock value
data: "some value"
}
}
render() {
return <div>
<TableTest
data={this.state.data}
header={[
{
name: "First Name",
prop: "firstName"
},
{
name: "Last Name",
prop: "lastName"
},
{
name: "Age",
prop: "age"
},
{
name: "Height",
prop: "height"
},
{
name: "Address",
prop: "address"
}
]}
/>
</div>;
}
}
class TableTest extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
header: this.props.header
}
console.log(this.state.data)
console.log(this.state.header)
}
render() {
return <div></div>;
}
}
ReactDOM.render(
<Example />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root">
</div>
Its an antipattern to set a state that is directly derivable from props, you would rather use the props directly. Also if you use the props to set state and you need to update state based on props change, you would also need to implement componentWillReceiveProps function

Categories