Lifting State Up And Functional Components - javascript

I'm having trouble with lifting state up and converting components to functions. What's wrong with my code.
Instructions: 1: Inside the JS section, create a class component named App. Inside its render() method, have it return the Welcome component. In the ReactDOM.render() method, change Welcome to App.
2: Lift the state from the Welcome component to App, so that the state is initialized inside of App's constructor.
3: Convert the Welcome component to a function component that returns the same welcome message as before. You will need to pass the bootcampName property of state from App to the Welcome component. It's up to you whether or not to destructure it.
class App extends Component {
constructor(props) {
super(props);
this.state = {
bootcampName: "Nucamp"
};
}
render() {
return (
<div className="App">
<Welcome {this.state.bootcampName}>;
</div>
);
};
}
function Welcome(props) {
return (
<h1>Welcome to {this.props.bootcampName}!</h1>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));

You have some errors there
You need to close the Welcome Component.
You need to name the prop
Destruct the props in because of this.state do not exist there.
Here the Code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
bootcampName: "Nucamp"
};
}
render() {
return (
<div className="App">
{ /**
* you need to close the Welcome Component
* you need to name the prop
*/}
<Welcome bootcampName={this.state.bootcampName}/>;
</div>
);
};
}
// Here destruct props to use it
function Welcome({bootcampName}) {
return (
<h1>Welcome to {bootcampName}!</h1>
);
}
ReactDOM.render(<App />, document.getElementById('root'));

Name the prop:
<div className="App">
<Welcome bootcampName={this.state.bootcampName} />
</div>

When you use functional components, you no longer need to use this. Try doing this instead:
class App extends Component {
constructor(props) {
super(props);
this.state = {
bootcampName: "Nucamp"
};
}
render() {
return (
<div className="App">
<Welcome bootcampName={this.state.bootcampName}>;
</div>
);
}
}
function Welcome({bootcampName}) {
return (
<h1>Welcome to {bootcampName}!</h1>
);
}

Related

React - Giving a function to my Child Component

I'm struggling to update the state of my Parent component from the Child. To do that, I want to pass as argument to my child a function that update the parent's state:
export class Login extends Component {
updateTurn() {
console.log("UPDATE")
this.setState(
prevState => {
UPDATING
}
);
}
render() {
ROUTING MANAGEMENT
<Route path="/play/pledge">
<Pledge data={this.state.data} updateTurn={this.updateTurn}/>
</Route>
ROUTING MANAGEMENT
}
}
export class Game extends Component {
constructor(props) {
super(props);
this.data = this.props.data;
}
render() {
return (
<div>
<div className={"game"}>
{ this.props.children }
</div>
<div className={"next"}>
<Link to={Launcher.getRandomGame()}>
<button className={"button"} onClick={this.props.updateTurn}>Next</button>
</Link>
</div>
</div>
)
}
}
Unfortunately, nothing happened, even to the console.log()
<Pledge data={this.state.data} updateTurn={this.updateTurn}/>
The problem is that you are passing the function to some component named Pledge, but you should use Game instead:
<Game data={this.state.data} updateTurn={this.updateTurn}/>

React child components don't re-render when mapped from an array

I'm loading some react components on demand (among with other information) depending on user input.
The components to render are kept in an array and the render method uses array.map to include the components.
The problem is, that if I trigger a forceUpdate() of the main app component, the mapped components won't update.
Code example: https://codesandbox.io/s/react-components-map-from-array-ekfb7
The dates are not updating because you are creating the instance of the component in your add function, and from then on you are referencing that instance without letting react manage the updates.
This is why storing component instances in state or in other variables is an anti-pattern.
Demonstration of the problem
Below I've created a working example still using forceUpdate just to prove what the issue is.
Notice instead of putting the component in state, I'm just pushing to the array to increase it's length. Then React can manage the updates correctly.
class TestComponent extends React.Component {
render() {
return <p>{Date.now()}</p>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.comps = [1];
}
add() {
this.comps.push(1);
this.forceUpdate();
}
render() {
return (
<div className="App">
<h1>Components map example</h1>
<p></p>
<h2>Static TestComponent (ok):</h2>
<TestComponent />
<h2>TestComponents mapped from an array (not ok):</h2>
{this.comps.map((comp, id) => {
return <div key={id}><TestComponent /></div>;
})}
<h2>All should update when the App component renders</h2>
<p>
<button onClick={() => this.add()}>Add TestComponent</button>
<button onClick={() => this.forceUpdate()}>forceUpdate App</button>
</p>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
<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="root"></div>
This is still a less than ideal solution. But it does show where the issue lies.
A better solution
If you need to know more about each component instance up front, you can make the array more complex.
I would also suggest using state to store the comps array, and removing forceUpdate completely.
class TestComponent extends React.Component {
render() {
return <p>{Date.now()} {this.props.a} {this.props.b}</p>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
comps: [{ a: 'a', b: 'b' }]
}
}
add = () => {
// add your custom props here
this.setState(prev => ({comps: [ ...prev.comps, { a: 'c', b: 'd' } ]}));
}
render() {
return (
<div className="App">
<h1>Components map example</h1>
<p></p>
<h2>Static TestComponent (ok):</h2>
<TestComponent />
<h2>TestComponents mapped from an array (not ok):</h2>
{this.state.comps.map((compProps, id) => {
return <div key={id}><TestComponent {...compProps} /></div>;
})}
<h2>All should update when the App component renders</h2>
<p>
<button onClick={() => this.add()}>Add TestComponent</button>
</p>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
<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="root"></div>
Now notice that each component in the map callback can have it's own unique set of props based on whatever logic you what. But the parts that should re-render will do so correctly.
In order to update in React, you have to put your data in the state and then setState.
setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering which means updating the screen with the new state.
import React from "react";
import "./styles.css";
class TestComponent extends React.Component {
render() {
return <p>{Date.now()}</p>;
}
}
export class App extends React.Component {
constructor(props) {
super(props);
this.state = {
comps: [<TestComponent />],
}
}
add = () => {
this.setState({ comps: this.state.comps.concat(<TestComponent />) })
}
render() {
return (
<div className="App">
<h1>Components map example</h1>
<p></p>
<h2>Static TestComponent (ok):</h2>
<TestComponent />
<h2>TestComponents mapped from an array (not ok):</h2>
{
this.state.comps.map((comp, id) => {
return <div key={id}>{comp}</div>;
})
}
<h2>All should update when the App component renders</h2>
<p>
<button onClick={this.add}>Add TestComponent</button>
</p>
</div>
);
}
}

Parent state change not updating child props

I'm trying to update a child based on the props provided by it's parent. The way it works right now is that the parent's state contains a variable called 'paused' which is provided to the child like this:
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
history: this.props.history,
paused: false,
}
}
render() {
let paused = this.state.paused
return (
<ChildContainer
graph={
<Child
paused={paused}
/>
}
/>
)
}
Child then uses it like this:
render() {
return (
<div>
{'paused:' + this.props.paused}
</div>
)
}
Paused is a boolean, the usage above is just for testing, since I couldn't get it to update where I want, the behaviour is the same like this.
Paused is being updated in the parent, but not the child.
I've read a lot of questions like this, but I'm at a loss.
Any help would be greatly appreciated.
I can't seem to find a problem with this based on the code you've provided. Here is working proof.
If ChildContainer has any logic that could interfere then I could be wrong, but as is, this works:
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
history: this.props.history,
paused: false,
}
}
render() {
let paused = this.state.paused // I agree with #Kuo-hsuan Hsu this is unnecessary
return (
<ChildContainer
toggle={() => this.setState({ paused: !this.state.paused })}
graph={<Child paused={paused}/>}
/>
)
}
}
class ChildContainer extends React.Component {
render() {
return (
<div>
{this.props.graph}
<button onClick={this.props.toggle}>Toggle</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>{'paused:' + this.props.paused}</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<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="root" />

How to pass function in another file in React

I have a function that is used to change the state of a react component but I'm trying to pass the function in another file. I get the error that the function I'm trying to pass (changeView) is not defined.
This is the App.js
export default class App extends Component {
constructor() {
super();
this.state = {
language: "english",
render: ''
}
}
changeView(view, e){
console.log(view);
this.setState({render: view});
}
_renderSubComp(){
switch(this.state.render){
case 'overview': return <Overview />
case 'reviews': return <Reviews />
}
}
render() {
const {render} = this.state
return <Fragment>
<Header language={this.state.language} />
<Hero />
<Navigation render={render}/>
{this._renderSubComp()}
</Fragment>;
}
}
I'm trying to pass the changeView method to the Navigation.JS component, so I can change the active link as well as render the components listed in the _renderSubComp method above.
import React from "react";
import "./navigation.css";
import { changeView } from "../app";
export default function Navigation() {
return <div className="navigation">
<a onClick={this.changeView.bind(this,
'overview')}>Overview</a>
<a>Reviews</a>
</div>;
}
How should I pass the function to another file so it's able to change the state of my component and render the component I need.
You can't import a method like that. You will pass your function like any other prop to your component and you use there.
I've changed a few things. Firstly, I define changeView function as an arrow one, so we don't need to bind it. Secondly, I pass this function to the component as a prop. Thirdly, I used this function there like:
onClick={() => props.changeView('overview')}
As you can see it is props.changeView not state.changeView
Just go through the official documentation a little bit more. You are confused about states, props and how to pass them to your components.
class App extends React.Component {
constructor() {
super();
this.state = {
language: "english",
render: ''
}
}
changeView = (view, e) => {
console.log(view);
this.setState({ render: view });
}
render() {
const { render } = this.state
return <div>
<Navigation render={render} changeView={this.changeView} />
</div>;
}
}
const Navigation = (props) => {
return <div className="navigation">
<a onClick={() => props.changeView('overview')}>Overview</a>
<a>Reviews</a>
</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>

Why doesn't this child component rerender?

I'm experimenting with ReactJS and I'm trying to understand how child component rendering is triggered. In ReactJS, if I set up an example like this:
var externalCounterVar = 10
class Counter extends React.Component {
constructor(props){
super(props);
this.state = props;
}
render() {
console.log('rendering counter')
return (
<div> {externalCounterVar} </div>
)
}
}
class Main extends React.Component {
constructor(props){
super(props);
}
handleClick() {
externalCounterVar += 1;
}
rerender(){
this.render();
}
render() {
console.log('rendering');
return (
<div>
<button onClick={this.rerender.bind(this)} />
<Counter counter={externalCounterVar} />
</div>
)
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
I'm not sure I understand why when you "rerender" it calls the render method of Main but not Counter? It seems like it should call both render methods since it's rendering Main and Counter is a child of Main.
So when rerender is called, 'rendering' will print but 'rendering counter' will not.
It looks like you're overlooking one of the main benefits of using React, namely how state works.
You never, ever need to call this.render within a React component
You should never set state dynamically, ie: this.state = ...
You should always use this.setState to set your state.
Rewritten, your code should look something like the following:
const externalCounterVar = 10
class Counter extends React.Component {
render() {
console.log('rendering counter')
return (
<div> {this.props.counter} </div>
)
}
}
class Main extends React.Component {
state = {
counter: externalCounterVar
}
handleClick() {
this.setState({counter: this.state.counter + 1});
}
render() {
console.log('rendering');
return (
<div>
<button onClick={this.handleClick.bind(this)} />
<Counter counter={this.state.counter} />
</div>
)
}
}
By calling this.setState, React automatically knows it needs to rerender your component, and as a result, all child components will also be rerendered.
Hope this helps!
In this case you don't have to use rerender method, also with purpose re-render all child components you need update state with method setState. And also accordingly to this you have to "move state up".
Here my example:
class Counter extends React.Component {
render() {
console.log('rendering counter');
return (<div> {this.props.counter} </div>);
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {counter: props.counter};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({counter: ++prevState.counter}));
}
render() {
console.log('rendering');
return (
<div>
<button onClick={this.handleClick} />
<Counter counter={this.state.counter} />
</div>
);
}
}
var externalCounterVar = 10;
ReactDOM.render(
<Main counter={externalCounterVar} />,
document.getElementById('root')
);
In some situations you can use this.forceUpdate() to call re-render.
But, if you can not do this, do not do.
https://facebook.github.io/react/docs/react-component.html#forceupdate

Categories