So I'm writing an application with a spring boot backend and react.js frontend. I am having an extremely annoying, and basic problem with react, and I'm not a very experienced JS developer...I'm a Java dev.
render() {
console.log(this.videoAreaData)
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>{libs}</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
I get the following output:
{
"libs": [
{
"name": "videos",
"library": null
}
]
}
So then I add
console.log(this.videoAreaData.libs)
and get this following output
[
{
"name": "videos",
"library": null
}
]
So to test printing the first element I should use this.videoAreaData.libs[0] obviously right? Apparently not
TypeError: this.videoAreaData.libs is undefined[Learn More]
What I want to do is iterate over the array in my JSX code using .map, but the object is literally always undefined! I've tried using setState with a const and all kinds of stuff! The data is passed from my parent app.js class which uses this:
componentDidMount() {
this.setState({isLoading: true})
libraryAccessor.listLibraries().then(libs => {
this.setState({ isLoading: false, show: false, libraries: libs })
})
}
libs is then pasted as a parameter into my code using a property like this
<LibraryLister libs={libraries} ref="libraries"></LibraryLister>
then it goes to the constructor of LibraryLister here
videoAreaData
constructor(videoAreaData) {
super(videoAreaData)
this.videoAreaData = videoAreaData
}
Now I assume all of that is done correctly, as it's non-null in my render method. If I put console.log(videoAreaData) within the JSX tags in a {} it's not null either, so it's definitely not supposed to be!
Here is what I finally want to do:
<Modal.Body>
{(this.videoAreaData.libs.map((library)=> {
return <p className="libname"> library.name </p>
}))}
</Modal.Body>
I feel like I'm doing something very very wrong here. That being said I have another project using the exact same stack, but made in typescript, and it works fine doing almost exactly this. Typescript is super irritating to use though, so I'd uh...prefer not to. Thanks in advance for any help
EDIT: Full code https://pastebin.com/daipM2gY
Also this alternate version of my render method prints the data as expected, so it...should not be undefined
render() {
console.log("PRINTING VID DATA: "+this.videoAreaData)
console.log(this.videoAreaData)
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>
{console.log("PRINTIN BODY")}
{console.log(this.videoAreaData)}
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
Output:
PRINTIN BODY
{
"libs": [
{
"name": "videos",
"library": null
}
]
}
So I uh figured out what I was doing wrong actually. Nothing to do with the state (the isLoading stuff on componentLoad worked fine as it did in another project) but the POJO I was sending.
So here's what happened: this.state.libraries[x] was in fact undefined, because this.state.libraries was actually an object. Containing another object called libraries which was the array from my POJO.
My POJO I was sending had "libraries" as the root object with an array inside of it. It was then being packed into the state as libraries, and so I had to use this.state.libraries.libraries[x]
Really stupid problem, but glad it's solved.
Thanks for your help guys!
Final code for those interested:
import React from 'react';
import { Modal, Button } from 'react-bootstrap';
import LibraryAccessor from '../accessors/LibraryAccessor'
import './navigator.css'
var libraryAccessor = new LibraryAccessor()
var librarylist = []
export default class LibraryLister extends React.Component {
state = { loadModal: false, libs: [] }
constructor(props) {
super(props)
this.state = {
...this.state,
libs: props
}
}
render() {
console.log(this.state.libs.libs[0])
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>
{this.state.libs.libraries.map(item=> {
console.log("ITEM")
})}
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
close = () => {
this.setState({ ...this.state, show: false });
}
open = () => {
this.setState({ ...this.state, show: true });
}
}
You have to study react
React’s props don’t work your post.
First you have to edit your videoAreaData constructor.
constructor(props) {
super(props)
this.videoAreaData = this.props.videoAreaData;
}
I recommend to read props in http://reactjs.org/docs
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { Button, Popover, Tooltip } from 'react-bootstrap';
// My components
import Navigator from './components/Navigator';
import LibraryLister from './components/LibraryLister';
import LibraryAccessor from './accessors/LibraryAccessor';
var loadingChildren = {};
var libraryAccessor = new LibraryAccessor();
var jsAccessor = new LibraryAccessor();
export default class App extends Component {
// React Component constructor parameter is `props`
constructor(props) {
super(props);
this.state = {
isLoading: true
}
}
open = () => {
this.refs.navigator.open()
}
openLibs = () => {
this.refs.libraries.open();
}
setLoading(refName, value) {
loadingChildren[refName].delete(refName);
if (loadingChildren.size < 1) {
// this.state = { isLoading: value }
// if you change state, you have to use setState; it's very very important.
// setState function is asynchronous because it relate performance.
this.setState({
isLoading: value
})
}
}
render() {
const { isLoading, libraries } = this.state;
console.log(libraries) // check please~!
if(isLoading) return <pre>Loading...</pre>
return (
<div className="App">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to Your Server</h1>
</header>
<Button onClick={this.open}>Show Navigator</Button>
<Button onClick={this.openLibs}>Show Libraries</Button>
<Navigator ref="navigator"></Navigator>
<LibraryLister libs={libraries} ref="libraries"></LibraryLister>
</div>
);
}
getLibraries = (libraries) => {
this.setState({
isLoading: false,
libraries
});
}
componentDidMount() {
libraryAccessor
.listLibraries()
.then(this.getLibraries);
}
}
import React, { Component } from 'react';
import { Modal, Button } from 'react-bootstrap';
import LibraryAccessor from '../accessors/LibraryAccessor';
import './navigator.css';
var libraryAccessor = new LibraryAccessor();
var librarylist = [];
export default class LibraryLister extends Component {
constructor(props) {
super(props);
this.state = {
loadModal: false
};
this.videoAreaData = props.videoAreaData
};
render() {
console.log("PRINTING VID DATA: "+this.videoAreaData)
console.log(this.videoAreaData)
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
close = () => {
this.setState({ ...this.state, show: false });
}
open = () => {
this.setState({ ...this.state, show: true });
}
}
Related
I am new to React.js, and so far, I am loving it. I am still confused on the concept of stateful components, although. I am using Bootstrap tables to build my table, and my GET request for its data grab worked flawlessly. I am using the material-ui lib for my switch component as well (no need to reinvent the wheel here!)
Although, I am now trying to integrate a new column that will be a switch for each row in my table, and that, when toggled, changes the boolean of said switch to true/false, which will then send a PUT request down to my backend. I have not built my PUT request yet, as I cannot get this UI portion functioning. Here is my code so far, and the dumby UI works, but I don't know how to integrate the stateful render I defined in NodeTableContainer at <SwitchState/> and SwitchState(), into my definition at selectionRenderer: Switches in my NodeTable component. The stateful render does render a toggle switch under the table, essentially as its own independent component. But I want to integrate that toggle switch component in const selectRow = {mode: 'checkbox', clickToSelect: true,selectionRenderer: Switches}. Here is my code, and I hope my I have explained my issue well. I have Googled endlessly, but I believe my own ignorance has blocked my from discovering the answer I need.
Table Component (NodeTable)
import React from 'react';
import {
Row,
Col,
Card,
CardBody,
} from 'reactstrap';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search, CSVExport, ColumnToggle } from 'react-bootstrap-table2-toolkit';
import paginationFactory from 'react-bootstrap-table2-paginator';
import 'chartjs-plugin-colorschemes';
import Switches from './Switch'
const columns = OMIT
const defaultSorted = [
{
dataField: 'id',
order: 'asc',
},
]
const TableWithSearch = (props) => {
const { SearchBar } = Search;
const { ExportCSVButton } = CSVExport;
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
selectionRenderer: Switches
}
return (
<Card>
<CardBody>
<h4 className="header-title">OMIT</h4>
<p className="text-muted font-14 mb-4">OMIT</p>
<ToolkitProvider
bootstrap4
keyField="fqn"
data={props.data}
columns={columns}
columnToggle
search
exportCSV={{ onlyExportFiltered: true, exportAll: false }}>
{props => (
<React.Fragment>
<Row>
<Col>
<SearchBar {...props.searchProps} />
</Col>
<Col className="text-right">
<ExportCSVButton {...props.csvProps} className="btn btn-primary">
Export CSV
</ExportCSVButton>
</Col>
</Row>
<BootstrapTable
{...props.baseProps}
bordered={false}
defaultSorted={defaultSorted}
pagination={paginationFactory({ sizePerPage: 5 })}
selectRow={selectRow}
wrapperClasses="table-responsive"
/>
</React.Fragment>
)}
</ToolkitProvider>
</CardBody>
</Card>
);
};
export default TableWithSearch;
Switch Component
// #flow
import React from 'react';
import 'chartjs-plugin-colorschemes';
import './Switch.css'
import Switch from '#material-ui/core/Switch';
export default function Switches({ isOn, handleToggle }) {
return (
<div>
<Switch
checked={isOn}
onChange={handleToggle}
name="checkedA"
inputProps={{ 'aria-label': 'secondary checkbox' }}
/>
</div>
);
}
Parent Component (NodeTableContainer)
import axios from 'axios';
import React, { Component, useState } from 'react';
import Switch from './Switch';
import App from './index';
export default class MainComp extends React.Component {
state = {
nodesData: [],
chartRef: [],
conn: [],
switchstate: [],
}
componentDidMount() {
axios.get('OMIT')
.then(res => {
const nodestate = res.data.map(x => x.nodestate);
for (var i = 0; i < nodestate.length; i++) {
if (nodestate[i] == 'up') {
nodestate[i] = true;
}
else {
nodestate[i] = false;
}
}
this.setState({ nodesData: res.data, switchstate: nodestate });
})
}
render() {
return (
< >
<App data={this.state.nodesData} checked={this.state.switchstate} />,
<SwitchState />
</>
)
}
}
function SwitchState() {
const [value, setValue] = useState(false);
console.log(value)
return (
<div className="app">
<Switch
isOn={value}
onColor="#EF476F"
handleToggle={() => setValue(!value)}
/>
</div>
);
}
Also, my SwitchState component is in a dumby form as you will see, until I can see the log showing its boolean state changing. Also, nodestate in the NodeTableContainer was my pathetic try at pulling data via the same state data. That is nonfunctional as you will also see. I will build the state properly once I can get this figured out, or you wonderful individuals aid me in this as well. Again, I am showing my ignorance here, so if there is an easier way, or if I am using an odd flavor of libs for this, please let me know. I want to learn and thrive. If you have a solution of your own, that's a completely different flavor, I plea to you to share it! Thank you all!
I figured this out for react-bootstrap. I fat arrowed in the formatter, and passed the state to formatExtraData. I then pass state from my component that holds all state, and it works flawlessly. Time to integrate my PUT request in with the event handler!
Below are my changes in code:
Table Component
export default class TableComp extends React.Component
formatter: (cell, row, index, extra) => {
if (cell === 'up') {
cell = true
}
else {
cell = false
}
return (
<div>
<Switch
checked={cell}
onChange={extra.handle}
name={row.name}
inputProps={{ 'aria-label': 'secondary checkbox' }}
/>
</div>
)
},
formatExtraData: { handle: this.props.handleClick }
Parent Component (Holds all state)
handleClick = (e) => {
var indexFound = this.state.data.findIndex(y => y.name === e.target.name.toString())
let data= [...this.state.data];
let item = { ...data[indexFound] }
if (item.state === 'up') {
item.state = 'down'
}
else {
item.state = 'up'
}
data[indexFound] = item
this.setState({ data})
}
Hi i have trouble to send method from parrent to child in react. I did this before and it works ... Why it no works anymore?
I am rendering this:
<div>
<ScheduleSelectModal colupdate={this.updateColumn.bind(this)} subject={this.state.selectedSubject} subjectslist={this.state.mySubjects} show={this.state.modalShow} onHide={this.changeModalState.bind(this)}>
</ScheduleSelectModal>
</div>
This is my method:
updateColumn(newSubject,dayId){
console.log("tu som");
console.log(this.state.schedule);
}
My modal:
ScheduleSelectModal extends Component {
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("modal props:");
console.log(this.props.subject);
}
update(){
console.log("updating...");
this.props.updatecolumn("test","test");
}
createList() {
let items = [];
if (this.props.subjectslist !== null)
this.props.subjectslist.map(subject =>
items.push(<Button key={subject.id} block className={"my-1"} onClick={this.update.bind(this)}>{subject.name} </Button>)
);
return items;
}
render() {
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
{this.renderHeader()}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<ButtonGroup vertical className={"w-100"}>
{this.createList()}
</ButtonGroup>
</Modal.Body>
</Modal>
);
}
}
And this is warning i am getting:
index.js:1375 Warning: Invalid value for prop `colupdate` on <div> tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.
Really don't know what the issue is. Thx for help
class ParentComponent extends Component {
passedFunction = () => {}
render() {
<ChildComponent passedFunction={this.passedFunction}/>
}
}
class ChildComponent extends Component {
render() {
<div onClick={this.props.passedFunction}></div>
}
}
You can use arrow function to avoid all the bindings. If you want to bind it, bind it in the constructor like so... in the parent component.
constructor() {
this.passedFunction = this.passedFunction.bind(this)
}
<ChildComponent passedFunction={this.passedFunction}/>
I could see that, in your child component you are using :
update(){
console.log("updating...");
this.props.updatecolumn("test","test");
}
but your props for that function is colupdate i.e. you should be using
update(){
console.log("updating...");
this.porps.colupdate("test","test");
}
Hope this helps!
Trying to learn React and Reactstrap and trying to get Modals to work. When I click the button, it should toggle the Modal, but right now it's giving me this error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of 'App'
Can't figure out what I'm doing wrong. Can someone help let me know where I'm going wrong here? Appreciate any help you can give.
I'd like to use the most recent versions of React and Reactstrap if possible.
Here's a link on Codepen: https://codepen.io/lieberscott/pen/ddYNVP
const { Button,
Container,
Modal,
ModalTitle,
ModalHeader,
ModalBody,
ModalFooter } = Reactstrap;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showModal: false
}
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal() {
console.log("hello");
this.setState({
showModal: !this.state.showModal
})
}
render() {
return (
<Container>
<Headline />
<Box />
<Button outline color="primary" onClick={this.toggleModal}>Click</Button>
<Modal isOpen={this.state.showModal} toggle={this.toggleModal} className="modal">
<ModalHeader>
<ModalTitle id="modalTitle">
Add a Recipe
</ModalTitle>
</ModalHeader>
<ModalBody>
Modal body
</ModalBody>
<ModalFooter>
Modal footer
</ModalFooter>
</Modal>
</Container>
);
}
}
class Headline extends React.Component {
render() {
return (
<div>
Recipes
</div>
);
}
}
class Box extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [
{
name: "Tofu tacos",
ingredients: [
"Shells",
"lettuce",
"tofu",
"paprika"
]
},
{
name: "Spaghetti",
ingredients: [
"pasta",
"sauce",
"salt"
]
}
] // end of items
} // end of this.state
} // end of constructor
render() {
const allitems = this.state.items.map(item => {
return (
<div>
{item.name}
</div>
);
})
return (
<div>
{allitems}
</div>
);
}
}
const app = document.getElementById("app");
ReactDOM.render(<App />, app);
I don't know how to share my code via codepen like you did, sorry.
I did these changes:
toggleModal() {
console.log("hello");
console.log( 'before setState: ', this.state );
this.setState({
showModal: !this.state.showModal
})
console.log( 'after setState: ', this.state );
}
The Button onClick event, when you do onClik={this.something} this this refers to the Button element. I changed to:
<Button outline color="primary" onClick={() => this.toggleModal()}>Click</Button>
When I do onClick={ () => this.something() } allows me to make use of the methods of my class.
Just look at the console.log output and you'll see clicking on button change showModal to true or false.
Take a look on cephalization's answer: https://forum.freecodecamp.org/t/difference-in-reactjs-onclick-function-binding/116027/2
It is far easier this way
add your state in the constructor example
this.state={showLogOut: false}
in your return all you need is to setState true example
<Button onClick={() => this.setState({showLogOut: true})}>Show modal</Button>
now all we need is the modal code since we have an state and a setState
<Modal isOpen={this.state.showLogOut} fade="false" toggle={() => this.setState({showLogOut: false})}>
<ModalHeader toggle={() => this.setState({showLogOut: false})}>Ready to leave?</ModalHeader>
<ModalBody>
<p>Press logout to end session.</p>
</ModalBody>
<ModalFooter>
<Button onClick={() => this.setState({showLogOut: false})}>Cancel</Button>
<Button color="info">Logout</Button>
</ModalFooter>
this should be enough to show your modal: I am assuming you are using react-strap and that you are importing the required elements but just to make sure here is what you need to import
import { Button, Modal, ModalBody, ModalFooter, ModalHeader} from 'reactstrap';
Reactstrap Modal don't show, because is need fade set on False. plz look my code who worked.
class Demoextends extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
fade: false
};
this.toggle = this.toggle.bind(this);
};
toggle() {
console.log("hello");
this.setState({
modal: !this.state.modal
});
console.log( 'after setState: ', this.state );
}
render() {
return (
<div>
<Button color="danger" onClick={this.toggle}>Launch</Button>
<Modal isOpen={this.state.modal} fade={this.state.fade } toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
<ModalBody>
</ModalBody>
<ModalFooter>
<Button onClick={this.toggle}>Do Something</Button>{' '}
<Button onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default Demoextends;
Of course You must add and part of reactstrap in your code
I'm new to React so bear with me.
I'm trying to create a modal component that will be triggered from a onClick() function from any global element i.e: link, button, span or whatever throughout the whole app.
import React from 'react';
import ReactDOM from 'react-dom';
const display = {
display: 'block'
};
const hide = {
display: 'none'
};
class Modal extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
toggle: false
}
}
toggle(event) {
this.setState(prevState => ({
toggle: !prevState.toggle
}));
}
render() {
return (
<div>
<button onClick={this.toggle}>Show Modal</button>
<div className="modal" style={this.state.toggle ? display : hide} >
<div className="modal-content">
{this.props.children}
<button onClick={this.toggle}>Close</button>
</div>
</div>
</div>
);
}
}
module.exports = Modal;
You can use react-bootstrap-modal:
npm install --save react-bootstrap-modal
Then in your component:
import React from 'react';
import Modal from 'react-bootstrap-modal';
export default class ModalExample extends React.Component {
constructor(props){
super(props);
this.state = {
open: false,
}
}
openModal = () => this.setState({ open: true })
closeModal = () => this.setState({ open: false })
render(){
return (
<div>
<button type='button' onClick={this.openModal}>Launch modal</button>
<Modal
show={this.state.open}
onHide={this.closeModal}
aria-labelledby="ModalHeader"
>
<Modal.Header>
<Modal.Title id='ModalHeader'>A Title Goes here</Modal.Title>
<div onClick={this.closeModal}>CLOSE HERE</div>
</Modal.Header>
<Modal.Body>
<p>Some Content here</p>
</Modal.Body>
<Modal.Footer>
// If you don't have anything fancy to do you can use
// the convenient `Dismiss` component, it will
// trigger `onHide` when clicked
<Modal.Dismiss className='btn btn-default'>Cancel</Modal.Dismiss>
// Or you can create your own dismiss buttons
<button className='btn btn-primary' onClick={this.closeModal}>
CLOSE HERE TOO
</button>
</Modal.Footer>
</Modal>
</div>
)
}
}
For further reference, please go here:
https://github.com/jquense/react-bootstrap-modal
You may also need to include bootstrap-CSS file if necessary! Please post here some errors if any, thanks
I am building an app with React v0.14.6 . The desired functionality is to click on the GoalItem to show the ModalItem. The goal-item has an 'a' tag with attribute onClick that sets {this.state.showModal: true}. The ModalItem is passed GoalItem's this.state.showModal via a showModal attribute.
To change the state of the ModalItem component such that this.state.showModal: this.props.showModal, I use setState() in componentWillReceiveProps.
The strange behavior is that the 'a' tag in GoalItem needs to be clicked twice to make the modal appear.The goal is for just one click to suffice.
Thank you in advance for your help. Below is the relevant code.
//goal-item.jsx
var React = require('react');
var ModalItem = require('./modal-item');
module.exports = React.createClass({
getInitialState() {
return {
name: this.props.goal.name,
nameChanged: false,
showModal: false
}
},
open() {
this.setState({ showModal: true });
},
render() {
return <a className="list-group-item"
key={this.props.goal.id}
onClick={this.open}>
<ModalItem goal={this.props.goal} showModal={this.state.showModal} />
</a>
}
});
//modal-item.jsx
var React = require('react');
var Modal = require('react-bootstrap/lib/modal');
var Button = require('react-bootstrap/lib/button');
module.exports = React.createClass({
getInitialState() {
return {showModal: false };
},
componentWillReceiveProps() {
this.setState({ showModal: this.props.showModal });
},
close() {
this.setState({ showModal: false });
},
render() {
return (
<div>
<Modal show={this.state.showModal} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>{this.props.goal.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{this.props.goal.description}</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.close}>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
});
In the componentWillReceiveProps you will get new props as argument.
So it should be:
componentWillReceiveProps(newProps) {
this.setState({ showModal: newProps.showModal });
}
Basically this is a place where you can compare new props from argument with old prop by accessing them using this.props, and then perform needed updates of state.
See documentation: componentWillReceiveProps