I have a React.Component and i want to call this static function from the many different React.Component.
class Categories extends React.Component {
constructor(props) {
super(props);
this.getCheckedCategories = this.getCheckedCategories.bind(this);
this.state = {
checked: [],
unchecked: []
};
}
static getCheckedCategories() {
return this.state.checked;
}
}
So I tried to connect the function.
import Categories from './product/checkboxes';
class FullWidthGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
const { classes } = this.props;
const checkedCategories = Categories.getCheckedCategories();
}
}
static functions can't access this, i.e. static method calls are made directly on the class and are not callable on instances of the class. You can read more on that here.
So in your case you could do:
class Categories extends Component {
state = {
checked: [],
unchecked: []
};
static getCheckedCategories = (klass) => {
return klass.state.checked
}
render() {
return (
<div>{Categories.getCheckedCategories(this)}</div>
);
}
}
Working example here.
That's the purpose of static function. It cannot access the instance (this).
You can have multiple instances of class but only one static function/property.
As a workaround (depend on what you want to do), you can use static property to store your state:
class Categories extends React.Component {
constructor(props) {
super(props);
Categories.state = {
checked: [],
unchecked: []
};
}
static getCheckedCategories() {
return Categories.state.checked;
}
}
However it won't work with setState since it is an instance method.
Imagine situation when you have muptiple Categories components and each one has different checked/unchecked categories. What would then Categories.getCheckedCategories() function returns?
If you want to have shared state (checked categories), I would recommend you to pull out the state out of the component. For example store it in parent component and pass it as props to child components. Or use state library like redux. You could also use react's context to manage shared state.
Related
I'm new to React working on an existing React component (that appears to be built in an older style - no hooks).
I want to read and set state within a handler function. I have the following code:
export default class MyComponent extends React.Component {
static defaultProps = {
data: {}
};
constructor(props) {
super(props);
// Other states
this.state.myState = false;
};
handleMyChange() {
if (!this.state.myState) {
console.log("hello world");
}
}
However I get the error Cannot read properties of undefined.
I've tried various like state.myState but am not really sure what I should be doing.
Can anyone point me in the right direction?
In order to have this context in your function, you will need to bind it in the constructor first
Here is a small example is taken from the official doc:
import React from "react";
export default class SayHello extends React.Component {
constructor(props) {
super(props);
this.state = { message: "Hello!" };
// This line is important!
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert(this.state.message);
}
render() {
// Because `this.handleClick` is bound, we can use it as an event handler.
return <button onClick={this.handleClick}>Say hello</button>;
}
}
I inherit from the react component of Cell. But the linter swears: The "props" property does not exist in the "Creative" type. The same is true for this.state and this.setState.
export default class Creative extends Cell<Props> {
constructor(props) {
super(props);
}
render() {
const { rowData: { creative }, rowNumber } = this.props;
How to fix it?
The Creative class inherits Cell so you have to add to the Creative class and in its props extend from the other props of the other component.
interface CreativeProps extends CellProps { //Its own props}
interface CreativeState { //Its state }
export default class Creative extends React.Component<CreativeProps , CreativeState> {
constructor(props) {
super(props);
}
render() {
const { rowData: { creative }, rowNumber } = this.props;
}
However, these are already implicit in Creative as long as you have declared them in Cell. The above (//...) is if you'd like to add props and state extra
You have an example here: https://codesandbox.io/s/react-typescript-tujz6
Am new to reactjs and am implementing a to do app using controlled component but am getting an error. Type Error: this.state is null!
Have you initialized your state in a constructor? You can do this by creating a constructor function in your class, e.g.
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {todoList: []};
}
Then you can add your todo's by doing this:
let todoListChange = this.state.todoList;
todoListChange.push("Clean my toilet");
this.setState({todoList: todoListChange});
You have not initialized your state. Default state is null.
class App from React.Component {
constructor (props) {
super(props);
this.state = { todos: [] }
}
render () {
// your implementation
}
}
I tried to create a container component that might change its inner component over time.
class Container extends React.Component {
constructor(props) {
super(props)
this.state = { content: new FooComponent() }
}
render() {
return <div>
{ this.state.content }
</div>
}
}
I get errors like:
Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state}). If you meant to render a collection of children, use an array instead.
I tried { [this.state.component] }, but get same error.
Are you able to put components from this.state into the JSX that is returned from render()? If not, how do you implement a container? I'm thinking of creating a container something like iOS's UINavigationController, where I can push and pop components in a stack, with only the top one rendered to the screen.
You can use JSX: this.state = { content: <FooComponent/> }
Which is React.createElement() behind the scenes.
class Container extends React.Component {
constructor(props) {
super(props)
this.state = { content: <FooComponent/> }
}
render() {
return <div>{this.state.content}</div>;
}
}
Or refer to Creating React Elements.
For reference here are more readable forms:
class Container extends React.Component {
state = {
content: <FooComponent />
};
render() {
return <>{this.state.content}</>;
}
}
function Container() {
const [content, setContent] = useState(<FooComponent />);
return <>{content}</>;
}
Can a React component render another component stored in it's state?
Yes you can!
There is two ways of doing it
1
import FooComponent from '..'
...
this.state = {
content: <FooComponent/>
}
...
render(){
return (
<div>{this.state.content}</div>
)
}
or
2
import FooComponent from '..'
...
this.state = {
content: FooComponent
}
...
render(){
const {
content: BarComponent
} = this.state
return (
<div>
<BarComponent />
</div>
)
}
I get errors like:
Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state}). If you meant to render a collection of children, use an array instead.
This happens because when you do new FooComponent() it returns an object and you can't render an object.
When using JSX, will be transpiled to React.createElement(FooComponent) and this is renderable.
When using only FooComponent, you store a reference to it and only in the render method you create a JSX that will become React.createElement(BarComponent)
The second aproach is good when the component is comming from props and you depending on the situation, you want to give it some props.
Working Example
The main question probably already answered there.
Notice: Limited functionality - storing only components will be a simple stateless view/screen stack/buffer.
The problem can be much wider. This solution won't be equal to saving previous states, changes ... no props/params, no history, no context, state reversing ... but shared common state (redux store) can be an advantage - no old states, autoupdating the whole chain of stored views.
class Container extends React.Component {
constructor(props) {
super(props)
this.state = { content: this.FooComponent() }
}
FooComponent(){
return(<FooComponent/>)
}
render() {
return <div>
{ this.state.content }
</div>
}
}
What is the right way to declare default props in react so that when I call map on a prop that is asynchronously assigned using redux I do not get an undefined error? Right now, with the following syntax I get an error when trying to assign trans_filter because data is undefined in the initial call to render.
class ContainerComponent extends React.Component {
static defaultProps = {
searchProps: {
data: []
}
};
constructor(props) {
super(props);
}
render(){
let trans_filter = JSON.parse(JSON.stringify(this.props.searchProps.data));
}
}
const mapStateToProps = (state) => ({
searchProps: state.searchProps
});
export default connect(mapStateToProps, {getTransactionsAll})(ContainerComponent);
Here's how you can declare default props when using the ES6 class syntax for creating ReactJS components:
class ContainerComponent extends React.Component {
constructor(props) {
super(props);
}
render(){
let trans_filter = JSON.parse(JSON.stringify(this.props.searchProps.data));
}
}
ContainerComponent.defaultProps = {
searchProps: {
data: []
}
};
export default ContainerComponent;
Additionally, there is another syntax for declaring defaultProps. This is a shortcut, but it will work only if your build has ES7 property initializers turned on. I assume that's why it doesn't work for you, because I see no issues with your syntax:
class ContainerComponent extends React.Component {
static defaultProps = {
searchProps: {
data: []
}
};
constructor(props) {
super(props);
}
render() {
let trans_filter = JSON.parse(JSON.stringify(this.props.searchProps.data));
}
}
export default ContainerComponent;
Edit: after you shared your mapStateToProps, yes, it has something to do with Redux!
The issue is caused by your reducer. You must declare initial state shape and moreover, you must specify the initial state in each reducer. Redux will call our reducer with an undefined state for the first time. This is our chance to return the initial state of our app.
Set initial state:
const searchPropsInitialState = {
data: []
};
Then, in your reducer when you manipulate searchProps do:
function yourReducer(state = searchPropsInitialState, action) {
// ... switch or whatever
return state;
}
For more details, see handling actions in the Redux docs.