Display items of array in subrow (react-table) - javascript

I am using react-table and need to create subrows with the data structure below. I have successfully created subrows for each object in the data array. However, each object in the data array contains another array "types."
How would i go about getting each row to list the "type" names as subrows?
My code so far is below:
Table:
import React from 'react';
import ReactTable from 'react-table';
const Table = (props) => {
const subComponent = row => {
return (
<div>
Names of "types" here respectively for each object in data array
(no column headers or anything needed)
</div>
);
};
return (
<ReactTable data={ props.data }
columns={ props.columns }
SubComponent={ subComponent } />
);
};
export default Table;
Data structure:
const data = [
{
id: '12345',
name: 'sports',
types: [
{
name: 'basketball',
id: '1'
},
{
name: 'soccer',
id: '2'
},
{
name: 'baseball',
id: '3'
}
]
},
{
id: '678910',
name: 'food',
types: [
{
name: 'pizza',
id: '4'
},
{
name: 'hamburger',
id: '5'
},
{
name: 'salad',
id: '6'
}
]
}
];

You can rewrite the getSubRows method on useTable optios.
Something like this:
const getSubRows = useCallback((row) => {
return row.types || [];
}, []);

Here is a good example on how to do it https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/archives/v6-examples/react-table-sub-components
From my best guess, your code will look like this:
import React from 'react';
import ReactTable from 'react-table';
const Table = (props) => {
const subComponent = row => {
return (
<div>
row.Original.types.map((type, idx) => (
<div>{{type.id}}</div>
<div>{{type.name}}</div>
))
</div>
);
};
return (
<ReactTable data={ props.data }
columns={ props.columns }
SubComponent={ subComponent } />
);
};
export default Table;

Related

Component doesn't return the new elements after updating state?

I have 3 elements and I want to add a new element by clicking on any div, but the problem is after adding new elements to the array they don't get rendered out of the component.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
let elements = [
{ id: 0, text: "first" },
{ id: 1, text: "second" },
{ id: 2, text: "third" }
];
const [state, setstate] = useState(elements);
function handleClick() {
elements.push({ id: 3, text: "xxx", checkBox: null });
setstate(elements);
console.log(state); //state shows 4 elememnt but they don't render in
}
return (
<div className="App">
{state.map((e) => (
// why this don't render the new elements?
<div onClick={handleClick}>{e.text}</div>
))}
</div>
);
}
in codesandbox https://codesandbox.io/s/beautiful-silence-c1t1k?file=/src/App.js:0-641
You should not mutate the state directly, it's not a good practice. Instead try as:
function handleClick() {
setstate(prevState => [
...prevState,
{ id: 3, text: "xxx", checkBox: null }
])
}
By doing this you are cloning the previous state of the array and adding that new element into the copy of the array what you can pass to setState function.
See the working CodeSandbox here.
You should not mutate the state directly
import React, { useState } from "react";
import "./styles.css";
const defaultElements = [
{ id: 0, text: "first" },
{ id: 1, text: "second" },
{ id: 2, text: "third" }
];
const newElement = {
id: 3,
text: "xxx",
checkBox: null
};
export default function App() {
const [state, setState] = useState(defaultElements);
function handleClick() {
setState((item) => [...item, newElement]);
}
return (
<div className="App">
{state.map(({ text }, index) => (
<div key={index} onClick={handleClick}>
{text}
</div>
))}
</div>
);
}

How to add new Properties to File object in React with state value dynamically

I hope to be descriptive, Let's say I have a Files Object Array
JSONfiledata = [
{
lastModified:123444,
name: 'file1',
size: 0,
type: ""
},
{
lastModified:123445,
name: 'file2',
size: 0,
type: ""
},
{
lastModified:123446,
name: 'file3',
size: 0,
type: ""
}
]
And I have a this component that receives that data through props
import React, {useState} from 'react'
const component = ({files}) => {
const [inputValue, setInputValue] = useState('')
const eventHandler = (e) => setInputValue(e.target.value)
const addNewKey = files.map(fileObj => Object.defineProperty(fileObj, 'newKey', {
value: inputValue
}))
return (
{
files.map(fileData => (<div>
{fileData.name}
<input value={inputValue} onChange={setInputValue} />
</div>))
}
)
}
How can I mutate the current files object and add a 'newKey' on each one depending on the inputValue, but independently from each other.
I mean, at position 0 let's say I write on the input "this is the file number one"
at position 1 "this is the file number two" and so on ....
At the end, the expected output will be
[
{
lastModified:123444,
name: 'file1',
size: 0,
type: "",
newKey: "this is the file number one"
},
{
lastModified:123445,
name: 'file2',
size: 0,
type: "",
newKey: "this is the file number two"
},
{
lastModified:123446,
name: 'file3',
size: 0,
type: "" ,
newKey: "this is the file number three"
}
]
I build a solution:
Build another component to manage every file individualy.
Like this:
import React, { useState } from 'react';
import { Map } from './Map';
export const MapList = ({ files }) => {
const [filesState, setFilesState] = useState([...files]);
const handleChange = nObject => {
/**You can compare with a unique id, preferably */
setFilesState(filesState => filesState.map(file => (file.name === nObject.name ? nObject : file)));
};
return (
<div>
{filesState.map(file => (
// If you have an ID you can send in this plance, to be more simple find the object in the handle function
<Map handleChange={handleChange} file={file} />
))}
<h2>Files Change</h2>
{filesState.map(file => (
<div>
<p>
{file.name} {file.newKey && file.newKey}
</p>
</div>
))}
</div>
);
};
In this wrapper component, you will update the entry array, with the handleChange function.
After you can build a component to manage your new key, for example:
import React, { useState } from 'react';
export const Map = ({ file, handleChange }) => {
const [input, setInput] = useState('');
const handleChangeKey = e => {
const { name, value } = e.target;
const nFile = { ...file, [name]: value };
setInput(value);
handleChange(nFile);
};
return (
<div>
<div>
<label htmlFor={file.name}>
<small>Input for: {file.name}</small>{' '}
</label>
<input id={file.name} name='newKey' value={input} onChange={handleChangeKey} type='text' />
</div>
</div>
);
};
It works for me, i think is a solution maybe not the best, but is a simple solutions.
const JSONfiledata = [
{
lastModified:123444,
name: 'file1',
size: 0,
type: ""
},
{
lastModified:123445,
name: 'file2',
size: 0,
type: ""
},
{
lastModified:123446,
name: 'file3',
size: 0,
type: ""
}
];
const fileNameToUpdate = 'file2';
const newKey = "file2Key";
const newArray = JSONfiledata.map((item) => {
if (item.name === fileNameToUpdate) {
return {...item, newKey: newKey };
} else {
return item;
}
});
console.log(`newArray==`, newArray);

React map over the array object

I'm quite new with react stuff, what I am trying is to generate some dynamic stuff using .map()
This is my component:
import React, { Component } from "react";
class DynamicStuff extends Component {
state = {
options: [
{ id: 1, optionOne: "OptionOne" },
{ id: 2, optionTwo: "OptionTwo" },
{ id: 3, optionThree: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option) => {
return {option}
})}
<span>{options.optionOne}</span>
<span>{options.optionTwo}</span>
<span>{options.optionThree}</span>
</>
);
}
}
export default DynamicStuff;
What I am doing wrong here and why the map is not generating expected result ?
Is it ok?
import React, { Component } from "react";
class DynamicStuff extends Component {
state = {
options: [
{ id: 1, value: "OptionOne" },
{ id: 2, value: "OptionTwo" },
{ id: 3, value: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option) => {
return <span>{option.value}</span>
})}
</>
);
}
}
export default DynamicStuff;
You have made your options object incorrectly. We need to have a same attribute over all the objects in the array.
class App extends React.Component {
state = {
options: [
{ id: 1, option: "OptionOne" },
{ id: 2, option: "OptionTwo" },
{ id: 3, option: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option, index) => (
<li key={index}>{option.option}</li>
))}
</>
);
}
}
Another thing, If you need to map an array. You don't need to have many spans. Having a one is just enough. The map function will iterate and give you all the things.
The map used here is actually to convert the js object into a react element, but your usage here is still a js object after the map conversion. The react element may be a <p key = {option.id}> {option. optionOne} </p>.
If there is a react element after the return in your map, it is correct.
{options.map((option) => {
return <p key = {option.id}> {option. optionOne} </p>
})}
or
{options.map((option) => <p key = {option.id}> {option. optionOne} </p>)}
YOu need to map and return the HTML element
return ({
options.map((option) => {
return `<span key={option.id}>${option. option}</span>`
})
});
You should do something like
render() {
const { options } = this.state;
return (
<div className="option-wrapper">
{options.length > 0 && options.map(option => {
return <span key={option.id}>{option.option}</span>
})}
</div>
);
}

List childs are not beeing updated correctly? (React / Preact)

I have the following component
import {h, Component} from 'preact'
import {getPersons} from '../../lib/datalayer'
import Person from '../person'
import {SearchInput} from '../search'
export default class Persons extends Component {
state = {
allPersons: [],
persons: [],
search: ''
}
async fetchData () {
try {
const allPersons = await getPersons()
this.setState({allPersons: allPersons.slice(), persons: allPersons.slice()})
} catch (error) {
....
}
}
constructor (props) {
super(props)
this.state = {
allPersons: [],
persons: [],
search: ''
}
this.fetchData()
}
onSearchInput = (search) => {
if (search === '') {
this.setState({search: search, persons: this.state.allPersons.slice()})
} else {
const persons = this.state.allPersons.filter(p => p.name.toLowerCase().includes(search.toLowerCase())).slice()
this.setState({search: search, persons: persons)})
}
}
render () {
const {persons} = this.state
return (
<div>
<SearchInput onInputChange={this.onSearchInput} placeHolder={'filter: name'} />
{persons.map(p => <Person person={p} />)}
</div>
)
}
}
The page renders a list of Persons and it has a filter on top. The filter seems to work fine, I tested it by doing a console.log of the results are just fine
The problem is that, if my list contains the objects:
[{name: 'thomas'}, {name: 'john'}, {name: 'marcus'}, {name: 'usa'}]
And I write in the search input: 'us'
The filter works fine and the result is:
[{name: 'marcus'}, {name: 'usa'}] \\ (the expected result)
In the page this objects are rendered
[{name: 'thomas'}, {name: 'john'}] \\ (wrong, this are the two first elements of the list)
If I search: 'joh'
The filter's result is
[{name: 'john'}] \\ (this is fine)
And the page renders only
[{name: 'thomas'}] \\ (the first element in the list)
It looks like the amount of elements that are rendered it's fine, but the content of the childs of the list is not beeing re-rendered.
Whats's wrong with my code?
React uses keys on the children of a list to determine which items changed and which of them remains the same. Since you have not specified a key on person, it takes index to be the key.
When index is key, you can see how shortening the list to two items, shows up the first two items in the list (the other indices are now missing). To get around this, you have to give a unique identifier on the person as key.
From your object, assuming name is unique (it usually isn't):
{persons.map(p => <Person person={p} key={p.name} />)}
Why are keys necessary - Docs
I cannot reproduce the error with react, did remove some unneeded slice and added unique id to each element (React will complain if you do not give each element a unique key and maybe so will preact).
const Person = React.memo(props => (
<pre>{JSON.stringify(props, undefined, 2)}</pre>
));
class Persons extends React.Component {
state = {
allPersons: [
{ name: 'aaa', id: 1 },
{ name: 'aab', id: 2 },
{ name: 'abb', id: 3 },
{ name: 'bbb', id: 4 },
{ name: 'bbc', id: 5 },
],
persons: [
{ name: 'aaa', id: 1 },
{ name: 'aab', id: 2 },
{ name: 'abb', id: 3 },
{ name: 'bbb', id: 4 },
{ name: 'bbc', id: 5 },
],
search: '',
};
onSearchInput = search => {
if (search === '') {
//slice not needed here
this.setState({
search: search,
persons: this.state.allPersons,
});
} else {
//filter already copies allPersons
const persons = this.state.allPersons.filter(p =>
p.name.toLowerCase().includes(search.toLowerCase())
);
this.setState({ search: search, persons: persons });
}
};
render() {
const { persons } = this.state;
return (
<div>
<input
type="text"
value={this.state.search}
onChange={e => this.onSearchInput(e.target.value)}
placeHolder={'filter: name'}
/>
{persons.map(p => (
<Person person={p} key={p.id} />
))}
</div>
);
}
}
ReactDOM.render(
<Persons />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Handling complex data structures in React

I'm new in React, and I would like to know If someone could help me with this.
I have an application like slack, where I can add a new Team and add a channel.
The problem is that is a complex DS and I have been trying to modify the state with new data passed through inputs, either in team and channel, but I have not had success
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
newTeamValue: "",
teams: [
{
newChannel: "",
name: "Team1",
channels: [
{
name: "Channel1",
index: 1
},
{
name: "Channel2",
index: 2
}
]
}
]
};
this.addTeam = this.addTeam.bind(this)
this.addChannel = this.addChannel.bind(this)
}
addTeam(e) {
e.preventDefault();
this.setState(prevState => ({
newTeam: "",
teams: [
...prevState.teams,
{
name: this.state.newTeam,
channels: []
}
]
}));
}
addChannel(e){
e.preventDefault()
this.setState(prevState => ({
newChannel:"",
teams: [
...prevState.teams,
{
channels: [...prevState, {
name: this.state.teams.newChannel
}]
}
]
}))
}
render() {
return (
<div>
<ul>
{this.state.teams.map(team => {
return (
<div>
<li>{team.name}</li>
<input onChange={e => this.setState({ newChannel: e.target.value })} value={this.state.newChannel} />
<button onClick={this.addChannel}>Add New Channel</button>
<ul>
{team.channels.map(channel => {
return (
<div>
<h2>{channel.name}</h2>
</div>
);
})}
</ul>
</div>
);
})}
</ul>
<input onChange={e => this.setState({ newTeam: e.target.value })} value={this.state.newTeam} />
<button onClick={this.addTeam}>Add new Team</button>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Something like this might help.
const newTeams = [...this.state.teams, { name: "Team3", channels: [] }]
this.setState({ teams: newTeams });
newTeams is an array than contains all the existing teams (...this.state.teams), and an additional team named Team3.
There are libraries (like immutablejs) that might be of use to you. I don't personally use them very often so I can't provide you with an exmaple, but might be something to look in to.
Edit:
You mentioned you're new to React, not sure if you're also new to JS. Incase you haven't seen the ... before, it's the Spread operator.
Edit2:
Re your comment about adding channels to existing teams
const newTeams = [
...this.state.teams,
{
name: "Team123",
channels: [
...this.state.Team123.channels,
{ name: "NewChannel", index: 123 }
]
}
];
this.setState({ team: newTeams });

Categories

Resources