React - Giving a function to my Child Component - javascript

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}/>

Related

Lifting State Up And Functional Components

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

this.props.parentprop.map is not a function (TypeError)

I'm having difficulties using the .map() method to iterate over the parent's state objects in a child component (ChildOne) to create multiple instances of another child component (ChildTwo).
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
listItems: [] // this list
}
render() {
return (
<div>
<ChildTwo listItems={this.state.listItems} />
</div>
}
for ChildOne, where the error can be found:
class TrackList extends React.Component {
render() {
console.log(this.props.items); // returns the array in parent state with no problem
return (
<div>
{this.props.items.map(item => { // this.props.items.map is not a function
return (
<ChildTwo item={item} key={item.id} />
)})
}
</div>
and finally, for ChildTwo:
class ChildTwo extends Component {
render() (
return (
<div>
<ChildOne items={this.props.listItems} />
</div>
I think your error is actually in ChildTwo? It's very confusing because your classes are not named the same as their rendering (TrackList, etc..).
You're rendering it like this, passing two different props named the same both named item:
<ChildTwo item={item} item={item.id} />
But the class definition is looking for a prop named listItems
class ChildTwo extends Component {
render() {
// ChildTwo was rendered with props `item`
return (
<div>
<ChildOne items={this.props.listItems} />
</div>
I would suggest using prop-types to show errors when the incorrect props are being passed.

Setup react route to use URL parameters and props' function

I've got a parent component with react-router, setup like this :
constructor(props){
super(props);
this.state = {
diner: false
};
this.updateFromInvite = this.updateFromInvite.bind(this);
}
updateFromInvite(Souper) {
this.setState({diner: Souper});
}
I can't figure out how to setup the route to have both URL parameters and be able to pass a function to update the parent's state from the children component...
<Route path="/Invitation/:NomParam1?/:NomParam2?"
component = {() => (<Invitation updateApp = {this.updateFromInvite} />)} />
I think it's the closest I got...
From children's component :
class Invite extends Component {
constructor(props){
super(props);
this.state = {
diner: this.props.match.params.NomParam1 ,
JSONInfo: this.props.match.params.NomParam2
};
}
componentDidMount() {
const { diner } = this.state;
const { JSONInfo } = this.state;
const { updateApp } = this.props;
updateApp(diner);
}
render() {
return (
<div className="Invite">
<div className="col-centered">
<VidPlay/>
</div>
</div>
);
}
}
export default Invite;
The component property of the route takes a component Class, not an instance of the component. I believe you are looking to use the render property, which takes a rendered component. Your visual component shouldn't be concerned with the routing details, so you can pass that in in the Route configuration like so:
<Route path="/Invitation/:NomParam1?/:NomParam2?"
render={({match}) => (
<Invitation
updateApp={this.updateFromInvite}
diner={match.params.NomParam1}
JSONInfo={match.params.NomParam2}
/>
)}
/>
Then, in the component, don't utilize state, as that's not really what it is for:
class Invite extends Component {
componentDidMount() {
const { diner, JSONInfo, updateApp } = this.props;
// Not exactly sure what is going on here... how you
// will use JSONInfo, etc
updateApp(diner);
}
render() {
return (
<div className="Invite">
<div className="col-centered">
<VidPlay/>
</div>
</div>
);
}
}
Also, I'm not exactly sure what the parent component is doing, and why it is passing both the route params and the function down to the child, only to have the child call it back... but that is probably out of the scope of the question.
Enjoy!
If finally got it (thanks to that answer and the official documentation):
I needed to add props as parameter of my render and
use it with {...props} inside the children element!
<Route path="/Invitation/:NomParam1?/:NomParam2?"
render={ (props) =>
(<Invitation updateApp = {this.updateFromInvite} {...props} />)
}
/>
With that, I have access to BOTH :
my custom props
generic props (match, location and history)

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

React - Passing State with this.props.children

So I am trying to pass some props from my top level component to a child component, I have done some searching online but cannot find anything that shows how I can pass this.props.children WITH some values my component's state. Here is my code.
Layout (Parent):
export default class Layout extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
render() {
const {location} = this.props;
console.log("layout");
return (
<div>
<Nav location={location}/>
<div className="container">
<div className="row">
<div className="col-lg-12">
{this.props.children}, data={this.state.data}
</div>
</div>
<Footer/>
</div>
</div>
);
}
}
When I call the "data" props in my next Component:
Home (Child):
//ON COMPONENT RENDER
componentDidMount = () => {
console.log("home");
console.log(this.props.data);
}
In my console it returns:
home
Undefined
Any pointers to how I should be doping this? Any help is appreciated, thank you in advance.
If you're trying to add a prop to the children directly, this won't really work since components are ment to be immutable. What you should do instead is create a map with clones of the children.
This blog post explains it fairly well: http://jaketrent.com/post/send-props-to-children-react/
And the relevent code snippets altered for your code:
class Layout extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
renderChildren() {
return React.Children.map(this.props.children, child => {
if (child.type === Child) {
return React.cloneElement(child, {
data: this.props.data
})
} else {
return child
}
});
}
render() {
const {location} = this.props;
console.log("layout");
return (
<div>
<Nav location={location}/>
<div className="container">
<div className="row">
<div className="col-lg-12">
{this.renderChildren()}
</div>
</div>
<Footer/>
</div>
</div>
);
}
}
Do a console.log on this.state.data in your Layout component. I'm thinking its undefined there too.
I think you need to set your state before the constructor super() call or statically outside the constructor.
Edit: So this.props.children is a react element? You need to clone it to pass it different props.
React.cloneElement(this.props.children, {
name: props.name
})

Categories