React stateless component not working - javascript

This is my code:
import React, { Component } from 'react'
import { BackButton } from 'components/button'
class LandingHeader extends Component {
render() {
const back = (props) => <BackButton forcedBackUrl={props.back.forcedBackUrl} />
return (
<div>
{back}
{this.props.children}
</div>
)
}
}
export default LandingHeader
If i put the <BackButton> component directly it works but if I use a stateless component and return it inside this one it wont. What im missing?
Im following the official documentation (https://facebook.github.io/react/docs/reusable-components.html) and I can't see whats wrong. Thanks.

Looking at the facebook documentation that you provided they give the example of :
const HelloMessage = (props) => <div>Hello {props.name}</div>;
ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
and not just returning {HelloMessage}
therefore replace
{back}
with
<Back />
and you should be good to go

You've declared a ReactClass but you aren't rendering it - you have to turn it into a ReactElement:
const Back = (props) => <BackButton forcedBackUrl={props.forcedBackUrl} />
return (
<div>
<Back {...this.props.back} />
{this.props.children}
</div>
);

You need to inject the props into the component
import React, { Component } from 'react'
import { BackButton } from 'components/button'
class LandingHeader extends Component {
render() {
const back = (props) => <BackButton forcedBackUrl={props.back.forcedBackUrl} />
return (
<div>
{back(this.props)}
{this.props.children}
</div>
)
}
}

Related

Why newly dynamically added Card components doesn't re render on form submit

I am trying to fetch data from GitHub user API and store the JSON in an array, and then and then loop through the array and create Card components dynamically on submission of form, but the Card components doesn't render even though the array is updated with the newly added user.
I have added relevant code files and image of React component tree on dev console before and after valid form submission. Thanks.
App.js
import React from "react";
import Form from "./Form";
import Cardlist from "./Cardlist";
import ProfileData from "../JSON";
export default class App extends React.Component {
testData = ProfileData();
state = {
profiles: this.testData,
};
addNewProfile = (profile) => {
this.setState({
profiles: [...this.state.profiles, profile],
});
};
render() {
return (
<div>
<h4>GITHUB PROFILE CARDS</h4>
<Form onSubmit={this.addNewProfile} />
<Cardlist profiles={this.state.profiles} />
</div>
);
}
}
Form.js
import React from "react";
export default class Form extends React.Component {
state = { userName: "" };
handleSubmit = (event) => (
event.preventDefault(),
fetch(`https://api.github.com/users/${this.state.userName}`)
.then((response) => {
return response.json();
})
.then((data) => {
this.props.onSubmit(data);
})
);
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="username"
value={this.state.userName}
onChange={(event) =>
this.setState({ userName: event.target.value })
}
/>
<button>Add Card</button>
</form>
</div>
);
}
}
Cardlist.js
import React from "react";
import Card from "./Card";
export default class Cardlist extends React.Component {
profile = this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
));
render() {
return (<div>{this.profile}</div>);
}
}
Card.js
import React from "react";
export default class Card extends React.Component {
render() {
return(
<div style = {{display: 'flex'}}>
<img src = {this.props.avatar_url} style = {{width: '100px', height: '100px'}}/>
<div>
<h6>{this.props.name}</h6>
<h6>{this.props.name}</h6>
</div>
</div>
);
}
}
component tree before
array before
component tree after
array after
You can change your CardList component to:
import React from "react";
import Card from "./Card";
export default class Cardlist extends React.Component {
render() {
return (
this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
))
);
}
}
The problem with your CardList component is that you have a class property (profile) which is initialized when the CardList component is created but the class property does not update its value when the props is updated.
PS: you can also convert your class property into a function
profile = () => (
this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
))
);
and the on render
render() {
return (<div>{this.profile()}</div>);
}
In Cardlist.js, instead of using a class variable, directly return the mapped elements.
import React from "react";
import Card from "./Card";
export default class Cardlist extends React.Component {
render() {
return this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
));
}
}
Working demo on Codesandbox.
Although your CardList is received a new props from parent and then get rerender, this.profile outside your render function, so it never get updated. Just put it inside your render function and it works fine
class Cardlist extends React.Component {
render() {
const profile = this.props.profiles.map((element) => (
<Card key={element.id} {...element} />
));
return <div>{profile}</div>;
}
}
codesanbox

How to share data between react classes?

How can I share the data value that I have from the handleClick function inside the Module class to the InputSelect class, all the classes are in the same file?? I'm not using Redux.
I can't use props because there is not a relationship between these classes..
Should I nest all the classes??
Any suggestion??
import React, { useState } from "react";
const array = [ ];
class InputSelect extends React.Component {
render() {
return (
<div>
{ 'Put it here........' }
</div>
)
}
}
class Module extends React.Component {
handleClick(e) {
console.log(e.currentTarget.textContent);
}
render() {
return (
<div
onClick={this.handleClick}
>
{this.props.id}
</div>
);
}
}
function App() {
return (
<div>
<Menu availableModules={array} />
</div>
);
}
export default App;
Correctly stated by #Federkun above, you should use React context.
Check the react docs here

React Context - setState with onClick inside Consumer

I have implemented React Context API and I am trying to update the state defined inside the Provider via an onClick function inside a child component.
This is what I have done so far, in the App.js I have:
import { createContext } from 'react';
const MyContext = React.createContext();
export class MyProvider extends Component {
state = {
currPrj: ''
}
handleBtnClick = prjCode => {
this.setState({
currPrj: prjCode
})
}
render() {
return(
<MyContext.Provider value={{
state: this.state
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
export const MyComsumer = MyContext.Consumer;
Inside my child component I have:
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { MyComsumer } from "../../index";
export class ProjectCard extends Component {
constructor(props) {
super(props);
this.state = {
// currPrj: ''
};
}
render() {
return (
<MyComsumer>
{(context) => (
<div className="card card-project">
<p>{context.state.currPrj}</p>
<div className="content">
<div className="author">
<Link to={ `projects/${this.props.code}/detail/info` } onClick={() => handleBtnClick(this.props.code) }>
<h4 className="title">
{this.props.title}
</h4>
</Link>
</div>
</div>
)}
</MyComsumer>
);
}
}
export default ProjectCard;
This way I get the following error
Failed to compile
./src/components/ProjectCard/ProjectCard.jsx
Line 32: 'handleBtnClick' is not defined no-undef
Search for the keywords to learn more about each error.
I don't get it why, because:
<p>{context.state.currPrj}</p>
throws no error...
Plus, is this.props.code passed correctly to the function?
Many thanks.
you can follow this:
My Fiddle: https://jsfiddle.net/leolima/ds0o91xa/1/
class Parent extends React.Component {
sayHey(son) {
alert('Hi father, this is son '+son+' speaking');
}
render() {
const children = React.Children.map(this.props.children, (child, index) => {
return React.cloneElement(child, {
someFunction: () => this.sayHey(index)
});
});
return (<div>
<b>Parent</b>
<hr />
{children}
</div>);
}
}
class Child extends React.Component {
render() {
return <div>
<button onClick={() => this.props.someFunction()}>Child</button>
</div>;
}
}
ReactDOM.render(
<Parent>
<Child />
<Child />
</Parent>,
document.getElementById('container')
);
<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="container">
</div>
There is linter error because handleBtnClick is not defined. It's a method of another class, not standalone function.
It's not available in the scope of context consumer function. If consumers are supposed to update the context, updater function should be a part of the context:
<MyContext.Provider value={{
state: this.state,
update: this.handleBtnClick
}}>
And used like:
context.update(this.props.code)

React Context Decorator/Subscriber?

Edit: Perhaps this could be referenced as a context subscriber?
I'm not even sure if this is the right concept that I'm trying to achieve. I want to be able to create a component that does the dirty work and just attaches context to the component that can the be consumed..
I've tried to find anything similar with no luck, which, leads me to believe I am not thinking of the right literal context of what it is I'm doing...
I've tried something like this:
import React, { Component } from "react";
export const Context = React.createContext();
export class ContextProvider extends Component {
state = {
scanning: false
};
render() {
return (
<Context.Provider
value={{
state: this.state,
handleClick: () => this.setState({
scanning: !this.state.scanning
})
}}
>
{this.props.children}
</Context.Provider>
);
}
}
And I trying to make it work with this..
import React from "react";
import { Context } from "./Context";
const WithContext = (children) => (props) => {
return (
<Context.Consumer>
{ state => (<children {...props} context={state} />) }
</Context.Consumer>
)
};
and then consuming with...
...
<WithContext>
<MyComponent />
</WithContext>
...
But, it just seems to fail or states that I'm returning a function instead of a react component..
Your WithContext component will not work like that... It needs to be a function that has the same functionality as the render function. like so:
import React from "react";
import { Context } from "./Context";
const WithContext = ({ children, ...props }) => (
<Context.Consumer>{state => React.Children.map(children, (child) => (
React.cloneElement(child, { context: state })
))}</Context.Consumer>
);
note that we traverse every direct child of the withContext children using React.Children.map (docs) and add a context prop to them by making use of React.cloneElement (docs). This keeps the child component's original props and shallow merges them into the second parameter passed to the function.
There are a bunch of little errors in your code for using context... Here is a complete example...
Let's say we have a structure where we have App.js -> Parent.js -> Child.js components... Instead of passing the data via props from App to Parent to Child we want to make use of the context API and avoid the prop drilling and have the Child consume the data directly...
Here is what that will look like:
context.js:
import React from 'react';
const Context = React.createContext();
export class Provider extends React.Component {
state = { name: 'Bob', age: 20 };
handleGrowUp = () => {
this.setState({ age: this.state.age + 1 });
};
render() {
return (
<Context.Provider
value={{
state: {
...this.state,
},
actions: {
growUp: this.handleGrowUp,
},
}}
>
{this.props.children}
</Context.Provider>
);
}
}
export const Consumer = Context.Consumer;
App.js:
import React from 'react';
import Parent from './Parent';
import { Provider } from './context';
const App = () => (
<Provider>
<Parent />
</Provider>
);
export default App;
Parent.js:
import React from 'react';
import Child from './Child';
const Parent = () => (
<div>
<Child />
</div>
);
export default Parent;
Child.js:
import React from 'react';
import { Consumer } from './context';
const Child = () => (
<div>
<Consumer>
{value => (
<div>
<p>{value.state.name}</p>
<p>{value.state.age}</p>
<button onClick={value.actions.growUp}>Grow Up</button>
</div>
)}
</Consumer>
</div>
);
export default Child;
Here is a working demo: https://codesandbox.io/s/9z06xzlyly

How can I access props passed to React.Component

I want to get some props made in the root layer of my react app:
import React from 'react'
import App, { Container } from 'next/app'
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
state = {
language: "pl"
};
render () {
const { Component, pageProps } = this.props
return (
<Container>
<Component lang={this.state.language} />
</Container>
)
}
}
so every new React.Component created should inherit those props. But I'm not sure how I can get them. Let's say I have another component which is <Nav/>.
Shouldn't I be able to get it via props.lang inside Nav.
When I try it says lang undefined.
I would suggest moving language to the React Context API
So this way you create a context
// context.js
import React from 'react';
export const LangContext = React.createContext('pl');
and provide it inside _app.js
// app.js
import React from 'react';
import App, { Container } from 'next/app';
import { LangContext } from '../context';
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
state = {
language: 'EN'
};
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<LangContext.Provider value={this.state.language}>
<Component {...pageProps} />
</LangContext.Provider>
</Container>
);
}
}
and whenever you need to access language value you dont need to pass it anymore. It will be available on LangContext. Example usage
// Nav.js
import Link from 'next/link';
import { LangContext } from '../context';
function Nav() {
return (
<LangContext.Consumer>
{lang => {
return (
<div className="site-nav">
<Link href="/">
<a>index</a>
</Link>
<Link href="/about">
<a>about</a>
</Link>
language = {lang}
</div>
);
}}
</LangContext.Consumer>
);
}
export default Nav;
This helps to solve the issue of passing lang props to pages and then to some specific components like Nav. Just wrap a component into a <LangContext.Consumer> if you need it.
Example index.js page
// index.js
import Nav from '../components/Nav';
export default () => (
<div>
<Nav />
<hr />
Welcome to index.js!
</div>
);
** One note: as far as I see you can only use <SomeContext.Provider> inside _app.js
I'm seeing a couple problems in your code example.
First, props are a property on your component, they should be accessed via this.props.
Here is a basic example of passing props to a child component:
import React, { Component } from 'react';
class App extends Component {
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} />
</div>
);
}
}
class Greeting extends Component {
render() {
return <h1>{this.props.greeting}</h1>;
}
}
export default App;
Using the code sample above, it would seem that your mistake was to use return <h1>{props.greeting}</h1>; instead of return <h1>{this.props.greeting}</h1>;
Second, it would appear that your component setup is a little off. I would expect your component declaration to look something like this:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
In your code sample, there's no constructor function and state doesn't appear to be set as a property of your component.
Inside of the example <Nav/> component, you must specify at least one argument in the component's function if you wish to access this.props. For example:
const Nav = (props) => ( <div> {this.props.lang} </div> )
Hope this helps!
Summary of my comments above:
Did you try props.lang, or, this.props.lang?
Because you need this.props.lang to access the property.
Hrm, just took a quick peek at my own code -- the initial state is set in constructor(props), and is defined like super(); this.state = (somestate);.
Because you need to set the state in the constructor of classes.

Categories