How does this React code translate to class representation? - javascript

I'm trying to understand how the code below, which is from Redux examples TODOMVC, can be written using the class notation.
The code is
const App = ({todos, actions}) => (
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
I tried the following but it doesn't work, I get Warning: App(...): When calling super() inApp, make sure to pass up the same props that your component's constructor was passed.
class App extends React.Component {
constructor({todos, actions}) {
super({todos, actions});
this.todos = todos;
this.actions = actions;
}
render() {
return(
<div>
<Header addTodo={this.actions.addTodo} />
<MainSection todos={this.todos} actions={this.actions} />
</div>
)
}
}

Whatever is passed to App is props. And ({ todos, actions }) is just destructuring from props. This should work:
class App extends React.Component {
render() {
const { todos, actions } = this.props;
return(
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
)
}
}
By setting this.todo = todos in constructor, you're setting an instance level property. Which means if the props changes later, Header and MainSection will not be updated.

You can simply do what React asks, pass the whole props to the superclass and get out the properties you want explicitly
class App extends React.Component {
constructor(props) {
super(props);
this.todos = props.todos;
this.actions = props.actions;
}
render() {
return(
<div>
<h1>Actions: {this.actions}</h1>
{/*<Header addTodo={this.actions.addTodo} />
<MainSection todos={this.todos} actions={this.actions} />*/}
</div>
)
}
}
ReactDOM.render(<App todos={[]} actions={'some action'} />, document.getElementById('app'))
<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="app"></div>

Related

How to display value of JSX function in Reactjs

I am quite new to jsx and while making my first Reactjs project today, I am facing this issue where I am unable to display the value of a function in the render method.
This is my parent page (Homescreen.jsx)
(I have commented on the sections where I felt that I have gone wrong)
import React, { Component } from 'react';
import Items from './Items'
class Render extends Component {
render() {
return (
<div>
<Items number="This is the first item !" /> {/*This is where I am adding props to Items*/}
<br>
<Items number="This is the second item !" />
</div>
);
}
}
export default Render;
This is the child page (Items.jsx)
import React, { Component } from 'react';
class Items extends Component {
// This is where I attempted to get the prop
Items = (props) => {
return (
<p>{props.number}</p>
)
}
render() {
return (
<div className="form-group">
{this.Items} {/*This is where I tried to display the output of "number" prop*/}
</div>
);
}
}
export default Items;
The end result is that the "Items" function has no output and I am getting an output which is the same as the output before adding the "Items" function.
Just use this.props.number in render method. (you can access all props from this.props). Try the snippet, there are two ways shown here.
class Items extends React.Component {
Items2 = (props) => {
return (
<p>{props.number}</p>
)
}
render() {
return (
<div className="form-group">
{this.props.number}
{/* alternatively */}
{this.Items2(this.props)}
</div>
);
}
}
class Render extends React.Component {
render() {
return (
<div>
<Items number="This is the first item !" />
<br/>
<Items number="This is the second item !" />
</div>
);
}
}
ReactDOM.render(<Render />, document.getElementById("app"))
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="app"></div>

React HOC with multiple components

I want to create a React HOC that would ideally receive two components instead of one wrapped component and toggle between them. That is, in the code below, instead of <h3>component one</h3> and <h3>component two<h3>, they would each represent child components. How would I be able to accomplish this? Some psuedo code for how I would write this HOC:
<HOC>
<ComponentOne />
<ComponentTwo />
</HOC>
<HOC
componentOne={<ComponentOne />}
componentTwo={<ComponentTwo />}
/>
hoc(componentOne, componentTwo)
class HOC extends React.Component {
constructor() {
super();
this.state = {
onClick: false,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({onClick: !this.state.onClick});
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me!</button>
{
this.state.onClick ?
<h3>component one</h3> :
<h3>component two</h3>
}
</div>
);
}
}
ReactDOM.render(<HOC />, app);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I am not sure if I understood you. Why do you need it to be HOC?
If you would pass components as props like that:
<HOC
componentOne={<ComponentOne />}
componentTwo={<ComponentTwo />}
/>
Then you would be able to access them using props.
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me!</button>
{
this.state.onClick ?
this.props.componentOne :
this.props.componentTwo
}
</div>
);
}
If a component has more than one child then this.props.children will be an array.
class HOC extends React.Component {
// ... rest of code ....
render() {
const { onClick } = this.state;
const { children } = this.props;
return !onClick ? children[0] : children[1];
}
}
Then use it like so:
<HOC>
<div>Child One</div>
<div>Child Two</div>
</HOC>
Obviously this will only work with two children but you could extend it by passing an integer to <HOC> through props to tell it what child to select.
Edit
After a quick look at the docs this is a better version of what I wrote above as this.props.children is not an array, it is an opaque data structure:
class HOC extends React.Component {
// ... rest of code ...
render() {
const { onClick } = this.state;
const children = React.Children.toArray(this.props.children);
return !onClick ? children[0] : children[1];
}
}

Unable to bind handler in React

I am trying to bind a method of a parent component to the state of its child component but I'm unable to get the desired result. I checked the value of 'this' in App component and it still points to the App component. Should it not be pointing to the ItemsList component since its being binded to it using bind()? Can someone please point out the mistake I'm making.
import React from 'react';
import {render} from 'react-dom';
class Item extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> {this.props.value} </div>;
}
}
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
itemArray: ['Work', 'Learn React']
}
this.props.adder.bind(this);
console.log(this.props.adder)
}
render() {
const items = this.state.itemArray.map(el=><Item key={el} value={el} />);
return (
<div>
<h2> To Do List </h2>
<ul>{items}</ul>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
}
addElement (data) {
let items = this.state.ItemList;
items.push(<Item value={data} />);
}
render() {
return (
<div>
<input type="text" ref={input=>this.input=input} />
<input type="button" value="Add" onClick={()=>this.addElement(this.input.value)}/>
<ItemList adder={this.addElement} />
</div>
);
}
}
render(<App />, document.getElementById('root'));
Should it not be pointing to the ItemsList component since its being binded to it using bind()?
Well,the step you following in not right one.
In App Component
You need to store the ItemList (child) component reference in App(parent) component.
<ItemList adder={this.addElement} bindChild = {(ref)=>this.itemList = ref}/>
In ItemList component,
you need to call bindChild method when ItemList component mounted.
componentDidMount(){
this.props.bindChild(this);
}
Now, in your App (parent) component, you have reference for ItemList (child) component in this.itemList property.
In App component, you can use this.itemList to update state of ItemList (child) component.
addElement(data) {
let items = this.itemList.state.itemArray;
console.log(items);
const newItem = <Item value={data} />
this.itemList.setState({ itemArray : [...items, newItem]})
}
Please check complete example on codesandbox
Though what you want is technically possible, this is a much more explicit easy to understand way to do it.
I re-factored your code so that the data flow only goes in one direction, from App to `Itemimport React from "react";
import { render } from "react-dom";
I also changed Item and ItemList to stateless components that take value and items as props respectively.
The main change is that App holds the state instead of ItemList
const Item = ({ value }) => <div>{value}</div>;
const ItemList = ({ items }) => (
<div>
<h2>To Do List</h2>
{items.map(item => <Item key={item} value={item} />)}
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: ["Work", "Learn React"]
};
}
addElement(value) {
this.setState(state => ({
items: [...state.items, value]
}));
}
render() {
return (
<div>
<input type="text" ref={input => (this.input = input)} />
<input
type="button"
value="Add"
onClick={() => this.addElement(this.input.value)}
/>
<ItemList items={this.state.items} />
</div>
);
}
}
render(<App />, document.querySelector("#root"));
Here is a CodeSandbox with your working app: https://codesandbox.io/s/4r4v0w5o94

How to update the props of a rendered react component from App.js?

I have a React component MoviesGallery.js with the following configuration:
class MoviesGallery extends Component {
constructor(props) {
super(props)
this.state = { currentImage: 0 };
this.closeLightbox = this.closeLightbox.bind(this);
this.openLightbox = this.openLightbox.bind(this);
this.gotoNext = this.gotoNext.bind(this);
this.gotoPrevious = this.gotoPrevious.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({movies_genre: nextProps.movies_genre})
}
I have rendered the component in my main App.js file like so:
class App extends Component {
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons"/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery/>
</div>
</MuiThemeProvider>
);
}
}
I want to update the props of my MoviesGallery component without recreating the component. Since I already added the componentWillReceiveProps() to MoviesGallery component, how can I make it so when 'Query' button is clicked, it will pass new props to the already rendered MoviesGallery and componentWillReceiveProps() should cause it to re-render since the state will change.
Just confused about the function that will change the props themselves on-click of the rendered MoviesGallery component.
Thanks in advance!
When a parent pass a new (value) prop to the child, the child component will call the render method automatically. There is no need to set a local state inside the child component to "store" the new prop.
Here is a small example of a Counter that receives a count prop and just displays it, while the parent App in this case will change the value in its state and pass the new value to Counter:
class Counter extends React.Component {
render() {
const { count } = this.props;
return (
<div>{count}</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
onClick = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
const { count } = this.state;
return (
<div>
<Counter count={count} />
<button onClick={this.onClick}>Add to counter</button>
</div>);
}
}
ReactDOM.render(<App />, 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>
you can use the 'state' for your MovieGallery.js props because the state is an object that changes and you must your code like below :
class App extends Component {
state = {
query : null
}
myFunction(query){
this.setState({query});
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons" onClick={this.myFunction = this.myfunction.bind(this)}/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery newProps = {this.state.query}/>
</div>
</MuiThemeProvider>
);
}
}
i hope it helps

Render Route after asynchronous call

I'm building my first React app. I'm trying to render some Routes from react-router-dom.
From the main component I call to my api to get a json object, then I update the state. The problem is my child component doesn't re-render after I have set the new state so I don't have props in the child components. I have used some functions like forcerender and componentWillReceiveProps but still doesn't work
I'm sure it's not a big problem but I have been trying to fix this for a couple of hours and I haven't been able to make it work.
Here is my latest attempt:
class DetectorEfficiencyCalculator extends Component {
constructor(props) {
super(props);
this.state = {
detectors: []
};
axios.get(`/detectors`)
.then(res => {
const detectors = res.data;
this.setState({ detectors });
console.log('state updated')
});
}
render() {
return (
<div className="DetectorEfficiencyCalculator">
<RoutesHandler detectors={this.state.detectors}/>
</div>
);
}
}
class RoutesHandler extends Component{
constructor(props) {
super(props);
this.state = { detectors: props.detectors } ;
}
componentWillReceiveProps(nextProps) {
this.setState({detectors:nextProps.detectors})
this.forceUpdate()
}
render() {
console.log('render')
return (
<div className="RoutesHandler">
<Switch>
<Route exact path='/frontend/Detectors' component={DetectorList} detectors={this.props.detectors}/>
<Route path='/frontend/Detectors/:number' component={DetectorDetail} detectors={this.props.detectors}/>
</Switch>
</div>
);
}
}
class DetectorList extends Component {
render () {
console.log('renderList')
if (!this.props.detectors) {
return null;
}
return (
<ul>
{this.props.detectors.map(u => {
return (
<Detector
id={u.id}
name={u.name}
single={u.single}
threshold={u.threshold}
angle={u.angle}
/>
);
})}
</ul>
);
}
}
<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>
Thanks in advance for your help :)
Try something like this:
class DetectorEfficiencyCalculator extends Component {
state={detectors:[]}
componentDidMount(){
const self = this;
axios.get(`/detectors`) //do it here...
.then(res => {
const detectors = res.data;
self.setState({ detectors });
console.log('state updated')
});
}
render() {
return (
<div className="DetectorEfficiencyCalculator">
<RoutesHandler detectors={this.state.detectors}/>
</div>
);
}
}
class RoutesHandler extends Component{
render() {
console.log('render')
return (
<div className="RoutesHandler">
<Switch>
<Route exact path='/frontend/Detectors' component={DetectorList} detectors={this.props.detectors}/>
<Route path='/frontend/Detectors/:number' component={DetectorDetail} detectors={this.props.detectors}/>
</Switch>
</div>
);
}
}
class DetectorList extends Component {
render () {
console.log('renderList')
if (!this.props.detectors) {
return null;
}
return (
<ul>
{this.props.detectors.map(u => {
return (
<Detector
id={u.id}
name={u.name}
single={u.single}
threshold={u.threshold}
angle={u.angle}
/>
);
})}
</ul>
);
}
}
<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>
Basically, you dont want to do any ajax calls or db access in the constructor, this is not the right way to do it in React since the constructor can be called multiple times. Instead use the React component lifecycle method componentDidMount to initiate the api call. In addition I used a variable (self) to hold a reference to the component (this) so I can use it in the axios promise handler.
Ok, I got into the solution: The route renders a function in which I render the component and load it with the props I need.
this.RenderDetectorDetail = (props) => {
return (
<DetectorDetail detectors={this.props.detectors}/>
);
};
<Route exact path='/frontend/Detectors' render={this.RenderDetectorList} />

Categories