This.props returning undefined? - javascript

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

Related

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

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>
);
}
}

Way to pass props and display in a list in react

I am encountering issues when trying to display the props in an ordered list. It only displays the initial value. When I add to the list, it was not updated.
Here is the source code
TodoList.js
class ToDoList extends React.Component {
render() {
return (
<div>
{this.props.data.map(list => {
return (
<ol key={list.uuid} > <input type="checkbox"></input>{list.uuid}- {list.text}</ol>
);
})}
</div>
);
}
}
export default ToDoList;
Todo.js
let data = [{ uuid: 100, text: "TEST" }, { uuid: 101, text: "TEST" }];
let id = 0;
class Todo extends React.Component {
handleAddItem = () => {
id = id + 1;
data.push({ uuid: id, text: this.refs.textInput.value });
console.log(data);
}
render() {
return (
<div>
<div>
<input type="text" ref="textInput"></input>
<button onClick={this.handleAddItem}>ADD TO LIST</button>
</div>
<ToDoList data={data} />
</div>
);
}
}
export default Todo;
Thank you.
When I add to the list, it was not updated.
It's because data is not a state variable and does not cause a re-render.
To fix, make it a state instead.
class Todo extends React.Component {
// make data and id a state variable
state = {
data: [{ uuid: 100, text: "TEST" }, { uuid: 101, text: "TEST" }],
id: 0,
}
...
}
Then pass state data to TodoList
<ToDoList data={this.state.data} />
WHEN UPDATING STATE:
Never mutate a state variable in your handler by using spread operator (...).
handleAddItem = () => {
// update the state using this.setState()
this.setState(prevState => ({
id: prevState.id + 1,
data: [
...prevState.data, // use spread operator
{ uuid: prevState.id + 1, text: this.refs.textInput.value }
]
}),
() => console.log(this.state.data) // pass 2nd arg to log state update
);
}
You should add state to ToDo component add use setSate() method to update state. Then the ToDo component and ToDoList component will re-render.
let data = [{ uuid: 100, text: "TEST" }, { uuid: 101, text: "TEST" }];
let id = 0;
class Todo extends React.Component {
state = {
data
}
handleAddItem = () => {
id = id + 1;
this.setSate({
data: [...this.state.data, { uuid: id, text: this.refs.textInput.value }]
});
console.log(this.state.data);
}
render() {
return (
<div>
<div>
<input type="text" ref="textInput"></input>
<button onClick={this.handleAddItem}>ADD TO LIST</button>
</div>
<ToDoList data={this.state.data} />
</div>
);
}
}
There is the doucment about Component State. Read it to learn more.

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.

Child component does not set the initial value passed from the parent: ReactJS

I am trying to implement a settings page where I have a global settings and some kind of child settings(in form of a slider).
I am unable to set the initial that is being passed from the parent.
I am handling the following scenarios:
1)When all of the child settings is on , then parents switch state should be turned on state
2)When any of the child settings is off, then parents switch state should be switched to pending
3)When all of the child settings is off, then parents switch state should be switched to off state
4) Also On click of button, I need to get the current state of all the child components.
If add an setState inside componentDidMount inside parent(may be API call will be written inside of it , so that initial states of the switches will be set accordingly and then be able to change) , The child switches should be able to get the state value right , But here it does not.
And I also see that toggling is happening in the wrong way. Here it is happening once you click on the already selected one which is ideally wrong
Have tried the following approach but it does not seem like working. For this , I am using react-multi-toggle for this toggle switch.
Can someone help here ?
Code Sandbox Link : https://codesandbox.io/s/react-multi-toggle-solution-yn3fh
App
import React from "react";
import ReactDOM from "react-dom";
import ChildSwitch from "./ChildSwitch";
import ParentSwitch from "./ParentSwitch";
import "./styles.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
parentVal: "disabled",
switch1Val: "disabled",
switch2Val: "disabled",
switch3Val: "disabled"
};
}
componentDidMount() {
this.setState({
switch1Val: "enabled",
switch2Val: "disabled",
switch3Val: "enabled"
});
}
onGetChildSwitchValues = () => {
console.log(this.state);
};
setChildSwitchValue = (whichSwitch, value) => {
this.setState(
prevState => Object.assign({}, prevState, { [whichSwitch]: value }),
this.setParentSwitchValue
);
};
setParentSwitchValue = () => {
const { switch1Val, switch2Val, switch3Val } = this.state;
const switchStates = [switch1Val, switch2Val, switch3Val];
const parent = switchStates.every(this.isEnabled)
? "enabled"
: switchStates.every(this.isDisabled)
? "disabled"
: "pending";
this.setState({ parentVal: parent });
};
isEnabled(value) {
return value === "enabled";
}
isDisabled(value) {
return value === "disabled";
}
render() {
const { parentVal, switch1Val, switch2Val, switch3Val } = this.state;
return (
<>
Parent Switch :{" "}
<ParentSwitch
parentSwitch={parentVal}
onSelect={this.setParentSwitchValue}
/>
Child Switches :
<ChildSwitch
childSwitch={switch1Val}
switchName={"switch1Val"}
onSelect={this.setChildSwitchValue}
/>
<ChildSwitch
childSwitch={switch2Val}
switchName={"switch2Val"}
onSelect={this.setChildSwitchValue}
/>
<ChildSwitch
childSwitch={switch3Val}
switchName={"switch3Val"}
onSelect={this.setChildSwitchValue}
/>
<button onClick={this.onGetChildSwitchValues}>Get Child Values</button>
</>
);
}
}
Parent
import MultiToggle from "react-multi-toggle";
import React from "react";
import "react-multi-toggle/style.css";
class ParentSwitch extends React.Component {
constructor(props) {
super(props);
this.state = {
options: [
{
displayName: "Disabled",
value: "disabled",
optionClass: "red"
},
{
displayName: "Pending",
value: "pending",
optionClass: "grey"
},
{
displayName: "Enabled",
value: "enabled",
optionClass: "green"
}
]
};
}
render() {
const { options } = this.state;
return (
<MultiToggle
options={options}
selectedOption={this.props.parentSwitch}
onSelectOption={() => {}}
/>
);
}
}
export default ParentSwitch;
Child
import MultiToggle from "react-multi-toggle";
import React from "react";
export default class ChildSwitch extends React.Component {
constructor(props) {
super(props);
this.state = {
options: [
{
displayName: "Disabled",
value: "disabled",
optionClass: "red"
},
{
displayName: "Enabled",
value: "enabled",
optionClass: "green"
}
],
selected: ""
};
}
componentDidMount() {
this.setState({ selected: this.props.childSwitch });
}
onSelectOption = selected => {
if (selected === "disabled") {
this.setState({ selected: "enabled" }, () =>
this.props.onSelect(this.props.switchName, "enabled")
);
} else {
this.setState({ selected: "disabled" }, () =>
this.props.onSelect(this.props.switchName, "disabled")
);
}
};
render() {
const { options, selected } = this.state;
return (
<MultiToggle
options={options}
selectedOption={selected}
onSelectOption={this.onSelectOption}
/>
);
}
}
A way to solve this is to control parent and child switches from master component.
Checkout the working forked codesandbox
APP
import React from "react";
import ReactDOM from "react-dom";
import ChildSwitch from "./ChildSwitch";
import ParentSwitch from "./ParentSwitch";
import "./styles.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
parentVal: "disabled",
switch1Val: "enabled",
switch2Val: "disabled",
switch3Val: "enabled"
};
}
componentDidMount() {
this.setParentSwitchValue();
}
onGetChildSwitchValues = () => {
console.log(this.state);
};
setChildSwitchValue = (whichSwitch, selected) => {
this.setState(
prevState => ({ ...prevState, [whichSwitch]: selected }),
this.setParentSwitchValue
);
};
setParentSwitchValue = () => {
const { switch1Val, switch2Val, switch3Val } = this.state;
const switchStates = [switch1Val, switch2Val, switch3Val];
let parent = "pending";
if (switchStates.every(val => val === "enabled")) {
parent = "enabled";
}
if (switchStates.every(val => val === "disabled")) {
parent = "disabled";
}
this.setState(prevState => ({ ...prevState, parentVal: parent }));
};
render() {
const { parentVal, switch1Val, switch2Val, switch3Val } = this.state;
return (
<>
Parent Switch :{" "}
<ParentSwitch
parentSwitch={parentVal}
onSelect={this.setParentSwitchValue}
/>
Child Switches :
<ChildSwitch
switchName={"switch1Val"}
selected={switch1Val}
onSelect={this.setChildSwitchValue}
/>
<ChildSwitch
switchName={"switch2Val"}
selected={switch2Val}
onSelect={this.setChildSwitchValue}
/>
<ChildSwitch
switchName={"switch3Val"}
selected={switch3Val}
onSelect={this.setChildSwitchValue}
/>
<button onClick={this.onGetChildSwitchValues}>Get Child Values</button>
</>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Parent
import MultiToggle from "react-multi-toggle";
import React from "react";
import "react-multi-toggle/style.css";
class ParentSwitch extends React.Component {
constructor(props) {
super(props);
this.state = {
options: [
{
displayName: "Disabled",
value: "disabled",
optionClass: "red"
},
{
displayName: "Pending",
value: "pending",
optionClass: "grey"
},
{
displayName: "Enabled",
value: "enabled",
optionClass: "green"
}
]
};
}
render() {
const { options } = this.state;
return (
<MultiToggle
options={options}
selectedOption={this.props.parentSwitch}
onSelectOption={() => {}}
/>
);
}
}
export default ParentSwitch;
Child
import MultiToggle from "react-multi-toggle";
import React from "react";
import "react-multi-toggle/style.css";
class ParentSwitch extends React.Component {
constructor(props) {
super(props);
this.state = {
options: [
{
displayName: "Disabled",
value: "disabled",
optionClass: "red"
},
{
displayName: "Pending",
value: "pending",
optionClass: "grey"
},
{
displayName: "Enabled",
value: "enabled",
optionClass: "green"
}
]
};
}
render() {
const { options } = this.state;
return (
<MultiToggle
options={options}
selectedOption={this.props.parentSwitch}
onSelectOption={() => {}}
/>
);
}
}
export default ParentSwitch;
The problem is you're not updating the local state when the childSwitch property changes. So it will stay on the disabled state. To achieve this you have to add the componentDidUpdate method or just directly use the property without any local state.
In ChildSwitch
componentDidUpdate(prevProps) {
if(prevProps.childSwitch !== this.props.childSwitch) {
this.setState({ selected: this.props.childSwitch });
}
}
Working fork: https://codesandbox.io/s/react-multi-toggle-solution-8xnf3

How to pass the event.target.value from one component to another component using react

I have below Simple Dropdown component
import React,{Fragment} from 'react';
export default class SimpleDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
listOpen: false,
selectedValue: 'calculated Measure'
}
this.selectedItem = this.selectedItem.bind(this);
}
selectedItem = (event)=> {
this.setState({
selectedValue: event.target.value
})
}
render() {
return (
<Fragment>
<select className="dd-wrapper" value={this.state.selectedValue} onSelect={this.selectedItem} >
{this.props.list.map((item) => (
<option className="dd-list-item" key={item.name} value={item.name}>{item.name}</option>)
)}
</select>
</Fragment>
);
}
}
I'm using this component in some other file as and its state as
this.state = {
measures: [{
name: 'calculated Measure',
},{
name: 'Base Measure'
}]
}
<SimpleDropdown title="create Measure" list={this.state.measures} />
Now, I want to pass the selected value from simple dropdown component to here, How can I do that?
The basic idea is to create your selectedItem function from the component you want the value in. Pass that function as a prop to your dropdown as a handler and update the value in the component where you need that value. Notice below onSelect is now calling this.props.handleSelect.
import React,{Fragment} from 'react';
export default class SimpleDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
listOpen: false,
}
}
render() {
return (
<Fragment>
<select className="dd-wrapper" value={this.state.selectedValue} onSelect={this.props.handleSelect} >
{this.props.list.map((item) => (
<option className="dd-list-item" key={item.name} value={item.name}>{item.name}</option>)
)}
</select>
</Fragment>
);
}
}
I created another component class based on what you posted above to help clarify. But this is where you will define the function and pass it into SimpleDropdown.
export class TestComponent extends React.Component {
state = {
selectedDropDownValue: '',
[{
name: 'calculated Measure',
},
{
name: 'Base Measure'
}
]
}
handleDropDownSelect = (event) => {
this.setState({ selectedDropDownValue: event.target.value });
}
render() {
return <SimpleDropdown
title="create Measure"
list={this.state.measures}
handleSelect={this.handleDropDownSelect}
/>
}
}
Hopefully this helped! Let me know if you need any more clarification.

Categories