I am trying to apply this example to my own code, but it seems that "ref" properties are obsolete in this form. Can anyone help me with this code? Right now i am getting "Cannot read property 'focus' of undefined", when clicking on filter
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
}];
class App extends React.Component {
state = {
searchText: '',
};
handleSearch = (selectedKeys, confirm) => () => {
confirm();
this.setState({ searchText: selectedKeys[0] });
}
handleReset = clearFilters => () => {
clearFilters();
this.setState({ searchText: '' });
}
render() {
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div className="custom-filter-dropdown">
<Input
ref={ele => this.searchInput = ele}
placeholder="Search name"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={this.handleSearch(selectedKeys, confirm)}
/>
<Button type="primary" onClick={this.handleSearch(selectedKeys, confirm)}>Search</Button>
<Button onClick={this.handleReset(clearFilters)}>Reset</Button>
</div>
),
filterIcon: filtered => <Icon type="smile-o" style={{ color: filtered ? '#108ee9' : '#aaa' }} />,
onFilter: (value, record) => record.name.toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
this.searchInput.focus();
});
}
},
render: (text) => {
const { searchText } = this.state;
return searchText ? (
<span>
{text.split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i')).map((fragment, i) => (
fragment.toLowerCase() === searchText.toLowerCase()
? <span key={i} className="highlight">{fragment}</span> : fragment // eslint-disable-line
))}
</span>
) : text;
},
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
filters: [{
text: 'London',
value: 'London',
}, {
text: 'New York',
value: 'New York',
}],
onFilter: (value, record) => record.address.indexOf(value) === 0,
}];
return <Table columns={columns} dataSource={data} />;
}
}
ReactDOM.render(<App />, mountNode);
code that I am trying to execute
https://hastebin.com/yipusowala.coffeescript
{
title: 'Rider',
key: 'rider',
width: '25%',
dataIndex: 'rider.name',
filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => (
<div className="custom-filter-dropdown">
<Input
ref={(input) => { this.searchInput= input; }}
placeholder="Search name"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={this.handleSearch(selectedKeys, confirm)}
/>
<Button type="primary" onClick={this.handleSearch(selectedKeys, confirm)}>Search</Button>
<Button onClick={this.handleReset(clearFilters)}>Reset</Button>
</div>
),
onFilter: (value, record) => record.rider.name.toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
this.searchInput.focus();
});
}
},
render: (text) => {
const {userFilterText} = this.state.userFilterText;
return userFilterText ? (
<span>
{text.split(new RegExp(`(?<=${userFilterText})|(?=${userFilterText})`, 'i')).map((fragment, i) => (
fragment.toLowerCase() === userFilterText.toLowerCase()
? <span key={i} className="highlight">{fragment}</span> : fragment // eslint-disable-line
))}
</span>) : text;
}
},
Your code seems just fine
(apart from this object destructuring:
const {userFilterText} = this.state.userFilterText;
which I'm guessing is a typo)
I created this sandbox based on the example you described and the ref for the <Input /> component as well as the .focus() trigger - seem to be working fine. (check out the console and you can see the Input ref being logged)
Hope this helps :)
Related
How can i render dynamic nested data in ReactJS? This array sample contains nested childs, each item have the same structure. There is a way to render this data recursively?
function App () {
const [data, setData] = useState([
{
id: '1',
name: 'demo1',
programParent: '',
childs: [
{
id: '2',
name: 'dem2',
programParent: '1',
childs: [
{
id: '4',
name: 'demo4',
programParent: '2',
childs: [
{
id: '5',
name: 'demo5',
programParent: '4'
}
]
}
]
},
{
id: '3',
name: 'demo3',
programParent: '1'
}
]
},
{
id: '6',
name: 'demo6',
programParent: ''
}
])
return (
<>
<div>
{
data.length &&
data.map(item => (
<div key={item.id}>
<h3>{item.name}</h3>
{
item.childs?.length && (
item.childs.map(child => (
<div key={child.id}>
<h4>-{child.name}</h4>
{
child.childs?.length && (
child.childs.map(childChild => (
<div key={childChild.id}>
<h5>--{childChild.name}</h5>
</div>
))
)
}
</div>
))
)
}
</div>
))
}
</div>
</>
)
}
export default App
You will need to render them recursively. Here is something to give you an idea, you can further customize the rendering.
import React from 'react';
import './style.css';
let Tree = ({ data }) => {
return (
<div>
{data.map((x) => {
return (
<div key={x.id}>
{x.name}
{!!x.childs?.length && (
<div style={{ marginLeft: 10 }}>
<Tree data={x.childs} />
</div>
)}
</div>
);
})}
</div>
);
};
export default function App() {
const [data, setData] = React.useState([
{
id: '1',
name: 'demo1',
programParent: '',
childs: [
{
id: '2',
name: 'dem2',
programParent: '1',
childs: [
{
id: '4',
name: 'demo4',
programParent: '2',
childs: [
{
id: '5',
name: 'demo5',
programParent: '4',
},
],
},
],
},
{
id: '3',
name: 'demo3',
programParent: '1',
},
],
},
{
id: '6',
name: 'demo6',
programParent: '',
},
]);
return (
<div>
<Tree data={data} />
</div>
);
}
Yes, with a recursive component:
const data = [
{
id: "1",
name: "demo1",
programParent: "",
childs: [
{
id: "2",
name: "dem2",
programParent: "1",
childs: [...]
},
...
]
},
...
];
export default function App() {
return <Node childs={data} name="root" />;
}
function Node({ childs, name }) {
return (
<div className="node">
<h2>{name}</h2>
{childs && childs.map((x) => <Node key={x.id} {...x} />)}
</div>
);
}
I would use a custom component for that demo
const Component = ({ item, level }) => {
const Heading = level <= 6 ? `h${level}` : "h6";
return (
<div>
<Heading>{item.name}</Heading>
{item.childs?.map((child) => (
<Component item={child} level={level + 1} />
))}
</div>
);
};
Usage
{data.length && data.map((item) => <Component item={item} level={1} />)}
I'm trying to create a SectionList from data received from an API. This API has the following structure for the ingredients that I want to display:
const ingredients = {
malt: [
{
name: 'Maris Otter Extra Pale',
amount: {
value: 3.3,
unit: 'kilograms',
},
},
{
name: 'Caramalt',
amount: {
value: 0.2,
unit: 'kilograms',
},
},
{
name: 'Munich',
amount: {
value: 0.4,
unit: 'kilograms',
},
},
],
hops: [
{
name: 'Fuggles',
amount: {
value: 25,
unit: 'grams',
},
add: 'start',
attribute: 'bitter',
},
{
name: 'First Gold',
amount: {
value: 25,
unit: 'grams',
},
add: 'start',
attribute: 'bitter',
},
{
name: 'Fuggles',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'middle',
attribute: 'flavour',
},
{
name: 'First Gold',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'middle',
attribute: 'flavour',
},
{
name: 'Cascade',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'end',
attribute: 'flavour',
},
],
yeast: 'Wyeast 1056 - American Aleā¢',
};
The desired result is the following using SectionList (with malt and hops as section headers):
Visual example of result
I've already tried to use functions like Object.values(), with no result at all. The code simply looks like the following, receiving the beer from the previous view(with a beer list):
const Detail = ({ route }) => {
const beer = route.params.beer;
const ingredientsFormatted = Object.values(beer.ingredients);
return(
<SectionList
sections={ingredientsFormatted}
renderItem={({ item }) => {
<Text>{item.name}</Text>; //Here there has to be the name of the different ingredients
}}
renderSectionHeader={({ section }) => <Text>{section}</Text>} //Here there has to be malt, hops or yeast
keyExtractor={(item) => item.name}
></SectionList>
)}
There are 2 problems in your ingredients data.
Format in value for yeast is not the same as the others.
Your data inserted into SectionList is not the format recommended by React-Native Offical docs.
To fix this, you can modify your API return / map the data after retrieved from server.
const Detail = ({ route }) => {
const beer = route.params.beer;
let sectionListTemplate = [];
for (let [key, value] of Object.entries(beer.ingredients)) {
//console.log(`${key}: ${JSON.stringify(value)}`);
if(!Array.isArray(value)){
//Handle case for 'yeast' which its data is not an array
sectionListTemplate.push({
title: key,
data: [{
name: value
}]
})
}
else{
//Map value for general cases
sectionListTemplate.push({
title: key,
data: value
})
}
}
return(
<SectionList
sections={sectionListTemplate}
renderSectionHeader={({ section }) => {
return(
//Here there has to be malt, hops or yeast
<Text style={{fontWeight: 'bold'}}>
{section.title}
</Text>
)
}}
renderItem={({ item }) => {
return(
//Here there has to be the name of the different ingredients
<Text>
{item.name}
</Text>
)
}}
keyExtractor={(item) => item.name}
>
</SectionList>
)
)}
so im trying to implement a search box with useState and useEffect. we have an array of objects and want to filter it according to our search term. here is my implementation:
import React, {useEffect, useState} from "react";
const array = [
{ key: '1', type: 'planet', value: 'Tatooine' },
{ key: '2', type: 'planet', value: 'Alderaan' },
{ key: '3', type: 'starship', value: 'Death Star' },
{ key: '4', type: 'starship', value: 'CR90 corvette' },
{ key: '5', type: 'starship', value: 'Star Destroyer' },
{ key: '6', type: 'person', value: 'Luke Skywalker' },
{ key: '7', type: 'person', value: 'Darth Vader' },
{ key: '8', type: 'person', value: 'Leia Organa' },
];
let available = []
const Setup = () => {
const [state, setState] = useState('');
useEffect(() => {
available = array.filter(a => a.value.startsWith(state));
},[state])
const show = state ? available : array;
return <>
<input value={state} onChange={e => setState(e.target.value)} type="text" className="form"/>
{show.map(a => {
return <Data id={a.key} key={parseInt(a.key)} value={a.value} type={a.type}/>
})}
</>
}
const Data = (props) => {
return <>
<div>
<p>{props.value}</p>
</div>
</>
}
export default Setup;
the problem starts when we give our search box a valid search term(like 'T'). i expect it to change the output accordingly(to only show 'Tatooine') but the output does not change.
meantime if you add another character to search term(like 'a' which would set our search term to 'Ta') it will output the expected result. in the other words, search term is not applied synchronously. do you have any idea why is that
The useEffect hook is triggered when the component mounts, rerenders or unmounts. In your case, the change of the search field causes a rerender because of the change of the state. This results in your useEffect triggering after the state change and is too late for what you need.
If you type "Ta" into your field, you'll see it works, but it appears as if the search is one step behind.
You can simply remove the use of useEffect and filter when you render. This means you can also remove the whole logic around the available and show variables:
const Setup = () => {
const [state, setState] = useState("");
return (
<>
<input
value={state}
onChange={(e) => setState(e.target.value)}
type="text"
className="form"
/>
{array
.filter((a) => a.value.startsWith(state))
.map((a) => (
<Data
id={a.key}
key={parseInt(a.key, 10)}
value={a.value}
type={a.type}
/>
))}
</>
);
};
There is some good information in the Using the Effect Hook docs.
You just add toLowerCase mehtod to your filter function. just like this :
import React, { useEffect, useState } from "react";
const array = [
{ key: "1", type: "planet", value: "Tatooine" },
{ key: "2", type: "planet", value: "Alderaan" },
{ key: "3", type: "starship", value: "Death Star" },
{ key: "4", type: "starship", value: "CR90 corvette" },
{ key: "5", type: "starship", value: "Star Destroyer" },
{ key: "6", type: "person", value: "Luke Skywalker" },
{ key: "7", type: "person", value: "Darth Vader" },
{ key: "8", type: "person", value: "Leia Organa" }
];
let available = [];
const Setup = () => {
const [state, setState] = useState("");
useEffect(() => {
available = array.filter((a) => a.value.toLowerCase().startsWith(state));
}, [state]);
const show = state ? available : array;
return (
<>
<input
value={state}
onChange={(e) => setState(e.target.value)}
type="text"
className="form"
/>
{show.map((a) => {
return (
<Data
id={a.key}
key={parseInt(a.key)}
value={a.value}
type={a.type}
/>
);
})}
</>
);
};
const Data = (props) => {
return (
<>
<div>
<p>{props.value}</p>
</div>
</>
);
};
export default Setup;
and here is the working example : here
You can simply just pull out useEffect.
import React, { useState } from 'react';
const array = [
{ key: '1', type: 'planet', value: 'Tatooine' },
{ key: '2', type: 'planet', value: 'Alderaan' },
{ key: '3', type: 'starship', value: 'Death Star' },
{ key: '4', type: 'starship', value: 'CR90 corvette' },
{ key: '5', type: 'starship', value: 'Star Destroyer' },
{ key: '6', type: 'person', value: 'Luke Skywalker' },
{ key: '7', type: 'person', value: 'Darth Vader' },
{ key: '8', type: 'person', value: 'Leia Organa' },
];
let available = [];
const Setup = () => {
const [state, setState] = useState('');
available = array.filter(a => a.value.startsWith(state));
const show = state ? available : array;
return (
<>
<input
value={state}
onChange={e => setState(e.target.value)}
type='text'
className='form'
/>
{show.map(a => {
return (
<Data
id={a.key}
key={parseInt(a.key)}
value={a.value}
type={a.type}
/>
);
})}
</>
);
};
const Data = props => {
return (
<>
<div>
<p>{props.value}</p>
</div>
</>
);
};
export default Setup;
This must solve it
import React, { useEffect, useState } from "react";
const array = [
{ key: "1", type: "planet", value: "Tatooine" },
{ key: "2", type: "planet", value: "Alderaan" },
{ key: "3", type: "starship", value: "Death Star" },
{ key: "4", type: "starship", value: "CR90 corvette" },
{ key: "5", type: "starship", value: "Star Destroyer" },
{ key: "6", type: "person", value: "Luke Skywalker" },
{ key: "7", type: "person", value: "Darth Vader" },
{ key: "8", type: "person", value: "Leia Organa" }
];
const Setup = () => {
const [state, setState] = useState("");
const [available, setAvailable] = useState(array);
useEffect(() => {
setAvailable(array.filter((a) => a.value.startsWith(state)));
}, [state]);
return (
<>
<input
value={state}
onChange={(e) => setState(e.target.value)}
type="text"
className="form"
/>
{available.map((a) => {
return (
<Data
id={a.key}
key={parseInt(a.key)}
value={a.value}
type={a.type}
/>
);
})}
</>
);
};
const Data = (props) => {
return (
<>
<div>
<p>{props.value}</p>
</div>
</>
);
};
export default Setup;
index.tsx
data = {
room: [
{
id: 1,
name: 'room1'
},
{
id: 2,
name: 'room2'
},
{
id: 3,
name: 'room3'
}
],
student: [
{
id: 1,
room: 'room1',
name: 'josh'
},
{
id: 2,
room: 'room1',
name: 'jake'
}
]
}
const _ROOM = data['room'];
const _STUDENT = data['student'];
const form = {
config: [
{
label: "Room",
name: "room",
type: "select",
rule: yup.string().required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<SelectPicker
placeholder="Room"
data={
_ROOM && _ROOM.length > 0 ?
_ROOM.map(x => ({ label: x.name, value: x.id })) : []
}
style={{ width: '100%' }}
onChange={(val) => {
control.setValue('room', val);
}}
value={control.getValues()['room']}
/>
);
}
},
{
label: "Student",
name: "student",
type: "select",
rule: yup.string().required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<SelectPicker
placeholder="Student"
data={
_STUDENT && _STUDENT.length > 0 ?
_STUDENT.map(x => ({ label: x.name, value: x.id })) : []
}
style={{ width: '100%' }}
onChange={(val) => control.setValue('student', val)}
value={control.getValues()['student']}
/>
);
}
}]}
How to filter the student based on the room. for example I select the room1 then on the student it will filter which it has value room1. I try to filter inside the onchange in room but it doesn't work also not filtering or display the console log. also I used the state then set inside the onChange but it doesn't work also.
Take a look at this: https://react-hook-form.com/advanced-usage/#ConditionalControlledComponent
Basically, you can watch for changes in room and filter out the options in the student field.
const {room} = watch();
And in the SelectPicker, modify the data prop to:
data={
_STUDENT && _STUDENT.length > 0 ?
_STUDENT.filter(s => s.room === room).map(x => ({ label: x.name, value: x.id })) : []
}
I am having a little difficulty with my data. I was able to get all mapped data currently but without me clicking the categories, the data won't be displayed.
Here's an example:
const categoriesData = [
{
name: 'All',
label: 'All',
total: '18',
className: 'activeCategory'
},
{
name: 'Business Services',
label: 'Business_Services',
total: '18'
},
{
name: 'Design Services',
label: 'Design_Services',
total: '18'
},
{
name: 'Education Services',
label: 'Education_Services',
total: '18'
},
{
name: 'Finance Services',
label: 'Finance_Services',
total: '18'
},
{
name: 'IT Services',
label: 'IT_Services',
total: '18'
},
{
name: 'Legal Services',
label: 'Legal_Services',
total: '18'
},
{
name: 'Manufacturing',
label: 'Manufacturing',
total: '18'
},
{
name: 'Marketing',
label: 'Marketing',
total: '18'
}
];
const postsData = {
All: [
{ id: 1, name: 'Post 3' },
{ id: 2, name: 'Post 2' }
],
Business_Services: [
{ id: 1, name: 'Post 3' },
{ id: 2, name: 'Post 2' }
],
Design_Services: [
{ id: 1, name: 'Post 5' },
{ id: 2, name: 'Post 2' }
],
Education_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
Finance_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
IT_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
Legal_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
Manufacturing: [
],
Marketing: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 27675' }
]
};
function fakePostsApi(catName) {
return new Promise((resolve) =>
setTimeout(() => {
resolve(postsData[catName]);
}, 1000)
);
}
function Main() {
const [categories, setCategories] = React.useState(categoriesData);
const [catName, setCatName] = React.useState();
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
if (catName) {
fakePostsApi(catName)
.then(setPosts);
}
}, [catName]);
return (
<div>
{categories.length > 0 ? (
categories.map((category, i) => {
return (
<button key={i} onClick={() => setCatName(category.label)}>
{category.name}
</button>
);
})
) : (
<p>Loading...</p>
)}
<div>
{posts.length === 0 ? (
<p>No posts...</p>
) : (
posts.map((post) => <div key={post.id}>{post.name}</div>)
)}
</div>
</div>
);
}
ReactDOM.render(
<Main />,
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>
My main problem is: I want the values of all to be displayed first(just like active status) when page loads without being clicking the All button.
Set the default value of catName to be the label of the 0 item of the categories:
const [catName, setCatName] = React.useState(categories[0] && categories[0].label);
Or optional chaining (?.) if supported:
const [catName, setCatName] = React.useState(categories[0]?.label);
const categoriesData = [{"name":"All","label":"All","total":"18","className":"activeCategory"},{"name":"Business Services","label":"Business_Services","total":"18"},{"name":"Design Services","label":"Design_Services","total":"18"},{"name":"Education Services","label":"Education_Services","total":"18"},{"name":"Finance Services","label":"Finance_Services","total":"18"},{"name":"IT Services","label":"IT_Services","total":"18"},{"name":"Legal Services","label":"Legal_Services","total":"18"},{"name":"Manufacturing","label":"Manufacturing","total":"18"},{"name":"Marketing","label":"Marketing","total":"18"}];
const postsData = {"All":[{"id":1,"name":"Post 3"},{"id":2,"name":"Post 2"}],"Business_Services":[{"id":1,"name":"Post 3"},{"id":2,"name":"Post 2"}],"Design_Services":[{"id":1,"name":"Post 5"},{"id":2,"name":"Post 2"}],"Education_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"Finance_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"IT_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"Legal_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"Manufacturing":[],"Marketing":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 27675"}]};
function Main() {
const [categories, setCategories] = React.useState(categoriesData);
const [catName, setCatName] = React.useState(categories[0] && categories[0].label);
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
if (catName) {
fakePostsApi(catName)
.then(setPosts);
}
}, [catName]);
return (
<div>
{categories.length > 0 ? (
categories.map((category, i) => {
return (
<button key={i} onClick={() => setCatName(category.label)}>
{category.name}
</button>
);
})
) : (
<p>Loading...</p>
)}
<div>
{posts.length === 0 ? (
<p>No posts...</p>
) : (
posts.map((post) => <div key={post.id}>{post.name}</div>)
)}
</div>
</div>
);
}
function fakePostsApi(catName) {
return new Promise((resolve) =>
setTimeout(() => {
resolve(postsData[catName]);
}, 1000)
);
}
ReactDOM.render(
<Main />,
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>