Pass a component to another inside a props array - javascript

How to pass a relative component through props to another component to render it?
For example pass to component table from app.js a props with a component button with relative calls and just call it like in code bellow? Is it possible?
Error
Element type is invalid: expected a string (for built-in components) or a >class/function (for composite components) but got: object.
In the Table.js component
import React from "react";
import { Container } from "./styles";
const Table = ({ data, columns }) => {
return (
<Container>
<thead>
<tr>
{columns.map((header) => {
return <th>{header.text}</th>;
})}
</tr>
</thead>
<tbody>
{data.map((dado) => {
return (
<tr>
{columns.map((column) => {
var { Mask } = column;
console.log(Mask);
return (
<td>
<Mask />
</td>
);
})}
</tr>
);
})}
</tbody>
</Container>
);
};
export default Table;
App.js
import React from "react";
import "./styles.css";
import Table from "./Table";
export default function App() {
const data = [
{ name: "teste", age: 30, place: "brazil", place2: "brazil" },
{ name: "teste", age: 30, place: "brazil", place2: "brazil" },
{ name: "teste", age: 30, place: "brazil", place2: "brazil" }
];
const columns = [
{
dataField: "name",
text: "Nome",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
},
{
dataField: "age",
text: "Idade",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
},
{
dataField: "place",
text: "Localidade",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
},
{
dataField: "place2",
text: "2",
Mask: (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{data.name}
</button>
)
}
];
return (
<div className="App">
<Table data={data} columns={columns} />
</div>
);
}

Make following changes to Table.js :
import React from "react";
// import { Container } from "./styles";
const Table = ({ data, columns }) => {
return (
<>
<thead>
<tr>
{columns.map(header => {
return <th>{header.text}</th>;
})}
</tr>
</thead>
<tbody>
{data.map(dado => {
return (
<tr>
{columns.map((column, index) => {
let { Mask } = column;
console.log(column["Mask"]);
Mask = {
...Mask,
props: { children: dado[Object.keys(dado)[index]] }
};
/*------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^
above you can see how we can set the children props of button
*/
return <td>{Mask}</td>;
})}
</tr>
);
})}
</tbody>
</>
);
};
export default Table;
Full working example: Stackblitz

<Mask /> is applicable to function or class components only, so Mask should be either a function or a class. In the example above it is a React element, not a React component.
Please, take a look at this article to understand the difference.
So in order to render it, it is just necessary to use it as a value, like this:
{columns.map((column) => {
var { Mask } = column;
console.log(Mask);
return (
<td>
{Mask}
</td>
);
})}
UPDATE: I didn't fully understand the question at first. So in order to pass data to Mask, you can use an approach from another answer or turn it into a function like this:
const columns = [
{
dataField: "name",
text: "Nome",
Mask: ({ children }) => (
<button
type="button"
onClick={() => {
console.log("Do Something");
}}
>
{children}
</button>
)
},
...
];
and render it as you did in your question:
{data.map((dado) => {
return (
<tr>
{columns.map((column) => {
var { Mask, dataField } = column;
console.log(Mask);
return (
<td>
<Mask>
{dado[dataField]}
</Mask>
</td>
);
})}
</tr>
);
})}

Related

I'm making a forum builder using react-beautiful-dnd I have an object who has id and name and content I want only the content to be dragged

Hello dear community I'm working on Form builder using react-beautiful dnd and I'm trying at the first time to drag only the content but on the list the name will be displayed here is the code if someone can help me
import React from "react";
import Review from "./Review";
import { useState, useEffect } from "react";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
const data = [
{
name: "Input",
id: "1",
content: <input type="text" />,
},
{
name: "Button",
id: "2",
content: <button>I'm a button</button>,
},
{
name: "Image",
id: "3",
content: (
<img src="https://static.wikia.nocookie.net/adventuretimewithfinnandjake/images/e/e6/Site-logo.png/revision/latest?cb=20210530110654" />
),
},
{
name: "Select",
id: "4",
content: (
<select>
<option>Op1</option>
<option>Op2</option>
</select>
),
},
];
const reOrder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
function App() {
const [items, setItems] = useState([]);
useEffect(() => {
setItems(data);
}, []);
const onDragEnd = (result) => {
if (!result.destination) {
return;
}
const reOrderedItems = reOrder(
items,
result.source.index,
result.destination.index
);
console.log(reOrder);
setItems(reOrderedItems);
};
return (
<main>
<section className="container">
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="dragdr">
{(provided, snapshot) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div className="item">
<div>{item.name}</div>
</div>
</div>
)}
</Draggable>
))}
</div>
)}
</Droppable>
</DragDropContext>
</section>
</main>
);
}
export default App;
I 'll be so greatful if someone can change my dragging behaviour from name to content and thank you in advance

Filtering using React and Redux, that will not prevent rerendering

I am pretty new in React and Redux and I am stucked with one problem. My application is a simple ToDo application and I have 2 main pages (they are one component) plain notations and archived notations. This is my problematic code
import React from 'react';
import { Fragment } from 'react/cjs/react.production.min';
import { TableRow } from './TableRow';
import getNotes from '../services/sortNotes';
import { useDispatch, useSelector } from 'react-redux';
import { addNote, filterNotes } from '../redux/actions';
const Table = ({ isArchived, setCurrentNote }) => {
const notes = useSelector(state => {
const { notesReducer } = state;
return notesReducer.notes;
});
return (
<div className="top-table">
<table className="table">
<colgroup>
<col className="first-column" />
<col />
<col />
<col />
<col />
<col className="date-column" />
<col className="last-column" />
</colgroup>
<thead className="table-head">
<tr>
<td></td>
<td>Name</td>
<td>Created</td>
<td>Category</td>
<td>Content</td>
<td>Dates</td>
<td>{
!isArchived ? (
<Fragment>
<button className="table-body-button" id="placehoder">Edit</button>
<button className="table-body-button" id="archive-all">Archive</button>
<button className="table-body-button" id="delete-all">Delete</button>
</Fragment>
) : <button className="table-body-button" id="unarchive-all">Unarchive</button>
}
</td>
</tr>
</thead>
<tbody className="table-body" id="main-content">
{
notes.map(note => (
<TableRow key={note.id} note={note} setCurrentNote={setCurrentNote}>
</TableRow>
))
}
</tbody>
</table>
</div>
)
}
export default Table;
First, I have tried to use filter inside and it was working well, until I pointed, that my apllication have losted it's rerender ability. Next, I have tried to do something inside my reducer, but I don't now how exactly to do this. This is my reducer:
import { ADD_NOTE } from "./types";
const initialState = {
notes: [
{
id: 0,
name: "Shopping List",
created: 'May 20, 2020',
category: 'Task',
content: 'lorem ipsum bla-...',
dates: '',
archive: false
},
{
id: 1,
name: "The theory of evolution",
created: 'July 30, 2020',
category: 'Random_thought',
content: 'The evolution is...',
dates: '',
archive: false
},
{
id: 2,
name: "New Feature",
created: 'December 25, 2020',
category: 'Idea',
content: 'Implemented new feature',
dates: '',
archive: false
},
{
id: 3,
name: "Books",
created: 'February 10, 2021',
category: 'Task',
content: 'New startup',
dates: '',
archive: false
},
{
id: 4,
name: "William Gaddis",
created: 'September 9, 2021',
category: 'Task',
content: 'Meet William',
dates: '',
archive: false
},
{
id: 5,
name: "What about inventions?",
created: 'September 30, 2021',
category: 'Idea',
content: 'Try to develop time machine',
dates: '',
archive: true
}
],
}
export const notesReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_NOTE:
return {
...state,
notes: [...state.notes, action.note]
}
default:
return state;
}
}
So I will be very appreciated if you will help me, because I am really don't know how to solve this. Link to my full code:
https://github.com/radush98/task2
I have found the solution!
import React from 'react';
import { Fragment } from 'react/cjs/react.production.min';
import { TableRow } from './TableRow';
import getNotes from '../services/sortNotes';
import { useDispatch, useSelector } from 'react-redux';
import { addNote, filterNotes } from '../redux/actions';
const Table = ({ isArchived, setCurrentNote }) => {
console.log(isArchived)
const notes = useSelector(state => {
const { notesReducer } = state;
return notesReducer.notes;
});
return (
<div className="top-table">
<table className="table">
<colgroup>
<col className="first-column" />
<col />
<col />
<col />
<col />
<col className="date-column" />
<col className="last-column" />
</colgroup>
<thead className="table-head">
<tr>
<td></td>
<td>Name</td>
<td>Created</td>
<td>Category</td>
<td>Content</td>
<td>Dates</td>
<td>{
!isArchived ? (
<Fragment>
<button className="table-body-button" id="placehoder">Edit</button>
<button className="table-body-button" id="archive-all">Archive</button>
<button className="table-body-button" id="delete-all">Delete</button>
</Fragment>
) : <button className="table-body-button" id="unarchive-all">Unarchive</button>
}
</td>
</tr>
</thead>
<tbody className="table-body" id="main-content">
{
notes
.filter(note => isArchived ? note.archive : !note.archive)
.map(note => (
<TableRow key={note.id} note={note} setCurrentNote={setCurrentNote}>
</TableRow>
))
}
</tbody>
</table>
</div>
)
}
export default Table;
This is how it works (First, I`ve tried ) something like this
notes
.filter(note => isArchived ? note.archive === isArchived)
.map(note => (
<TableRow key={note.id} note={note} setCurrentNote={setCurrentNote}>
</TableRow>
))
But it prevented my rerendering, so I cahnged this filter to something like this
notes
.filter(note => isArchived ? note.archive : !note.archive)
.map(note => (
<TableRow key={note.id} note={note} setCurrentNote={setCurrentNote}>
</TableRow>
))

Accessing "More Details" when clicked in a React Table

I am still learning React. I have an array of fake data in a json file that I am using to populate a table that I made with react-table. I have the table as a component. I have added a "Details" button in a column on the table, and when it's clicked, I want a second component to appear on the screen below the table that contains additional details that I could not reasonable fit onto the table. I assume I need to do some sort of handleClick function that's called with the button's onClick, and use that handleClick to setState some information that will let access it through the Details component. On the table component, I did a forEach to assign everything it's own ID number in efforts to use that to somehow call upon that specific item for more details, but I'm not really sure how to utilize that.
App.js
import './App.css';
import Details from './components/Details'
import Table from './components/Table';
import React, { Component } from 'react';
class App extends Component {
state = {
}
handleClick(){
this.setState({
})
}
render() {
return (
<>
<Table />
<Details />
</>
)}
}
export default App;
Table.js
import React, { useMemo } from "react";
import Data from "../sample.json"
import { useTable, useSortBy, usePagination } from 'react-table'
import { Columns } from './Columns'
import './Table.css'
function Table() {
const columns = useMemo(() => Columns, [])
const data = useMemo(() => Data, [])
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
prepareRow,
} = useTable(
{columns, data, initialState: {pageSize: 5}},
useSortBy,
usePagination)
const { pageIndex } = state
Data.forEach((item, i) => {
item.id = i + 1
})
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)})}
</tr>
)})}
</tbody>
</table>
<div>
<center>
<span>
Page{' '}{pageIndex + 1} of {pageOptions.length}{' '}
</span>
<button
onClick={() => previousPage()}
disabled={!canPreviousPage}>
Previous
</button>
<button
onClick={() => nextPage()}
disabled={!canNextPage}>
Next
</button>
</center>
</div>
</>
)
}
export default Table;
Columns.js
export const Columns = [
{
Header: 'ID',
accessor: 'id'
},
{
Header: 'Payee Name',
accessor: 'Payee.Name'
},
{
Header: 'Payee Fax',
accessor: 'Payee.Fax'
},
{
Header: 'Payee Phone',
accessor: 'Payee.Phone'
},
{
Header: 'Link',
accessor: '',
Cell: props => <button onClick={() => {}}>Details</button>
},
]
Screenshot below is more or less what I want to see. Just a table, and the expanded details for the last item clicked on that table. Right now, I simply have some code mapping over all the data and giving them their own cards. But I just want one at a time, ideally. Current code mapping everything will be below.
enter image description here
import React from 'react';
import Data from '../sample.json'
function Details() {
return (
<>
{ Data.map(item => {
return(
<div className="item">
<div className="itemRow1">
<div>
<h4>{ item.Payee.Name }</h4>
<div>{ item.Payee.Phone }</div>
<div>{ item.Payee.Address.Address1 }</div>
<div>{ item.Payee.Address.Address2 }</div>
<div>{ item.Payee.Address.City }</div>
<div>{ item.Payee.Address.StateOrProvince }</div>
<div>{ item.Payee.Address.Country }</div>
<div>{ item.Payee.Address.PostalCode }</div>
<br/>
<div>{ item.Payee.Attention} </div>
<div>{ item.Payee.SubmissionDate }</div>
</div>
<div>
<h4>Payment Information</h4>
<div>{ item.Payment.PAN }</div>
<div>{ item.Payment.CVV }</div>
<div>{ item.Payment.Exp }</div>
</div>
<div>
<h4>Remittance</h4>
<div>{ item.Remittance.map(payor => {
return(
<div className="payor">
<div>{ payor.PayorName }</div>
<div>{ payor.PayorId }</div>
<div>{ payor.InvoiceNo }</div>
<div>{ payor.Amount }</div>
<br/>
</div>
)})}
</div>
</div>
</div>
<div>
{item.Remittance.map(payor => {
return (
<div className="description">
{payor.Description}
</div>
)})}
</div>
</div>
)})}
</>
);
}
export default Details;

How to access attribute of a function in another component in reactjs

I want to get an attribute from a function within a component. The function is called checkbox.js:
export default function Checkboxes() {
const [checked, setChecked] = React.useState(false);
const handleChange = (event) => {
setChecked(event.target.checked);
};
return (
<div>
<Checkbox
checked={checked}
onChange={handleChange}
color="primary"
inputProps={{ 'aria-label': 'primary checkbox' }}
/>
</div>
);
}
The component Checkbox multiple times in a table row. props.characterData is an array that has various attributes.
const TableBody = props => {
const rows = props.characterData.map((row, index) => {
return (
<tr key={index}>
<td>
<Checkbox />
</td>
<td>{row.task}</td>
<td>{row.desc}</td>
</tr>
)
})
return <tbody>{rows}</tbody>
}
What I want to do is to save the "checked" boolean value into the row.checked attribute every time it is changed, but nothing I have tried or searched up worked.
The row.checked comes from an array of characters where each character is initialized in the form:
{ task: '', desc: '', checked: false}
In the TableBody, it is mapped for each element in the array.
Any help would be greatly appreciated.
import React, { useState, useEffect } from 'react'
const Checkboxes =({checked, cbOnChange})=> {
const handleChange = (event) => {
cbOnChange(event.target.checked);
};
return (
<div>
<Checkbox
checked={checked}
onChange={handleChange}
color="primary"
inputProps={{ 'aria-label': 'primary checkbox' }}
/>
</div>
);
}
const TableBody = props => {
const [checkboxValue, setCheckboxValue] = useState({})
useEffect(() => {
setCheckboxValue({})
}, [JSON.stringify(props.characterData)])
const handleChange =(index, checked)=>{
setCheckboxValue((state)=>({
...state,
[index]: checked
}))
}
const rows = props.characterData.map((row, index) => {
return (
<tr key={index}>
<td>
<Checkboxes cbOnChange={(checked)=> handleChange(index, checked)} checked={checkboxValue[index] || false}/>
</td>
<td>{row.task}</td>
<td>{row.desc}</td>
</tr>
)
})
return <tbody>{rows}</tbody>
}
export default TableBody
you should write like this:
export default function App() {
const [characterData , setCharacterData] = React.useState([{
id: 1,
priority : true,
task: "task1",
desc: "desc1"
},
{
id: 2,
priority : false,
task: "task2",
desc: "desc2"
}])
const handleChangePriority = (id , value) => {
let cloneCharacterData = [...characterData]
const selectData = characterData.filter(itm => itm.id === id)[0];
const index = cloneCharacterData.findIndex(itm => itm.id === id)
selectData.priority = value;
cloneCharacterData[index] = selectData;
setCharacterData(cloneCharacterData)
}
return (
<div >
<TableBody characterData={characterData} handleChangeProps={handleChangePriority} />
</div>
);
}
TableBody:
const TableBody = props => {
const rows = props.characterData.map((row, index) => {
return (
<tr key={index}>
<td>
<Checkbox id={row.id} priority={row.priority} handleChangeProps={props.handleChangeProps} />
</td>
<td>{row.task}</td>
<td>{row.desc}</td>
</tr>
)
})
return <tbody>{rows}</tbody>
}
And Checkboxes:
export default function Checkboxes({priority , handleChangeProps , id}) {
// const [checked, setChecked] = React.useState(false);
const handleChange = event => {
// setChecked(event.target.checked);
handleChangeProps(id , event.target.checked)
};
return (
<div>
<Checkbox
checked={priority}
onChange={handleChange}
color="primary"
inputProps={{ "aria-label": "primary checkbox" }}
/>
</div>
);
}

Triggering the first list item while using List with map in React

I am displaying list item through map function. But I want the list item to automatically trigger itself and show something for instance i am just using console.log for that. So the first list item should automatically trigger it's onClick function. Is there anyway to acheive this in react?
...
import React, { useState } from "react";
import ListGroup from "react-bootstrap/ListGroup";
import Container from "react-bootstrap/Container";
import Button from "react-bootstrap/Button";
import "./App.css";
const App = () => {
const [data, useData] = useState([
{ list: "appelllll" },
{ list: "ballllslsss" },
{ list: "cattsssssss" },
{ list: "dogssssss" },
{ list: "eggssss" },
{ list: "fatssssssssssssssssssss" },
{ list: "goatssssssssssssssss" },
{ list: "heloooooooooooooooooo" },
{ list: "ieloooooooooooooo" },
{ list: "jelooooooooo" },
{ list: "kelooooooo" },
{ list: "leooo" },
{ list: "melosdsadsado" }
]);
return (
<Container className="p-3">
<ListGroup
className="list_menu"
horizontal
style={{
overflowX: "scroll"
}}
>
<button>+</button>
{data.map((data, i) => {
return (
<div>
<ListGroup.Item
className="list_item"
key={i}
onClick={() => console.log(data)}
>
{data.list}
</ListGroup.Item>
</div>
);
})}
<button> > </button>
</ListGroup>
</Container>
);
};
export default App;
...
working code here https://codesandbox.io/s/vigorous-rgb-koiwf?file=/src/App.js
I think it's better to do it like this:
onClick={() => {
if(i === 0)
console.log(data)
}}

Categories

Resources