I'm a little unfamiliar with class components in react.
I am trying to figure out how to pass down a prop via context. This state contains an array of 2 objects, representing 2 different users.
When this state is updated I would like to render certain data from these two objects (text).
Here is my code:
In Context:
const PropDrilling = createContext();
export default PropDrilling;
export const PropDrillingProvider = ({ children }) => {
const [fighters, setFighters] = useState([])
let contextData = {
fighters: fighters,
setFighters: setFighters
}
return(
<PropDrilling.Provider value={contextData} >
{ children }
</PropDrilling.Provider>
)
}
This is where I would like to render the component:
import PropDrilling from "contexts/PropDrilling"
class Landing extends React.Component {
state = {};
static contextType = PropDrilling
componentDidMount() {
const fighters = this.context
console.log('test', fighters)
}
render() {
return (
<>
...
<div className="icon icon-shape icon-shape-primary rounded-circle mb-4">
<i className="ni ni-check-bold" />
</div>
<h6 className="text-primary text-uppercase">
{fighters[0].name}
</h6>
I am getting this error message:
src\views\pages\Landing.js
Line 102:30: 'fighters' is not defined no-undef
Related
I am very new to react.js and I have been working on a component class (child) that has functions and a single state object and my end goal is to use this class in a parent class so it, in turn, can call its functions and update the state.
The problem I have been running into is that:
I wasn't aware of a component's lifecycle, and
I come from a heavy C# background
Meaning: I have been treating these component classes like I would any C# class instead of JavaScript. I know that now.
But I need help evaluating my approach and solving this issue I keep seeing:
This is my child Class component
import React, { Component } from 'react';
import axios from 'axios';
export default class ClassB extends Component {
constructor() {
super();
console.log("ClassB constructor got called");
this.state = {
users: [{ name: '', email: '' }]
};
}
getUsers() {
let URL = "https://localhost:5001/api/FooController/FooAction"
let myParam = 100;
axios.get(URL,
{
params: { myParam }
})
.then(response => {
// handle logic here...
}).catch(function (error) {
console.log('What happened? ' + error.response.data);
});
}
addUserData(name, email) {
this.setState(this.state, { users: [name, email] });
}
componentDidMount() {
console.log("ClassB componentDidMount got called");
}
render() {
console.log("ClassB render got called");
return ( null )
}
}
And in my parent class (Home.js) I am instantiating the child class (ClassB.js) and using its instance as such:
import React, { Component } from 'react';
import ClassB from './ClassB'
import ClassC from './ClassC'
const classBComponent = new ClassB();
export class Home extends Component {
static displayName = Home.name;
constructor() {
super();
}
componentDidMount() {
this.timerID = setInterval(() => {
classBComponent.getUserValues();
}, 3000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
render() {
myComponent.render();
return (
<div className="container">
<h1>My Other Component:</h1>
<div className="row">
<div className="col-sm">
<ClassC name={[{ component: classBComponent, info: ['John', 'john123#123.com'] }]} />
</div>
</div>
</div>
);
}
}
In this parent class I intend to call the "getUserValues() methods from the ClassB component. I also have another child component (ClassC), which is a functional component, and I want to pass the instantiated ClassB component so it can access its functions and states as well. But, in ClassC component, when I call "addUserData()" method it gives me the error I pasted above (see image).
Here is how I have my ClassC set up:
import React, { useEffect } from 'react';
import axios from 'axios';
const ClassC = (props) => {
// variables
let user = props.name[0].info[0];
let email = props.name[0].info[1];
// component
const component = props.name[0].component;
// renders component
function componentMount() {
component.addSimModelNodeInfo(user, email);
}
// leaves the component
function componentUnmount() {
}
useEffect(() => {
componentMount();
return () => {
componentUnmount();
}
}, [])
return (
<div className="card shadow-sm p-3 mb-5 bg-white rounded">
<div className="card-header">
<h5>{name}</h5>
<h5>{email}</h5>
</div>
</div>
);
}
export default ClassC;
I mentioned earlier how I didn't have a solid grasp on components' lifecycles. I placed those console.logs in ClassB only to realize that the only method getting called is the constructor. The componentDidMount() function never gets called and neither does the render(). Why is that? I know its linked to that error which is why my ClassB component never gets "mounted". What am I doing wrong? Many thanks in advance.
Here's an example of calling a parent component function from a child component. I'm using functional components for brevity, but this is totally possible with class-based as well.
If you're not familiar with functional components, you can consider props to be like this.props, and the function itself is similar to the render() method in classful components. There's more to it, but for this small example that should help you if you need to translate.
const ChildComponent = (props) => <button onClick={props.onClick} />
const ParentComponent = () => {
const onClick = () => {
console.log("Click Clack");
}
return <ChildComponent onClick={onClick} />;
};
How do I send property values from child component to parent component when the react app is loaded? I have a parent component called app.js, which renders a home component which contains a JSON component.
When the react app is loaded, my property JSONValue (defined in app.js) holds an empty string. When the react app is loaded, JSONValue in the parent component should contain the value already defined in the child JSON component. How do I manage to send these values from JSON component to APP component on initial render?
The only way I can do this as it is right now, is by changing the value in the react app --> json component. Then it sends the value from child to parent.
So:
The value of JSONValue is defined in JSON component, and is only applied to the JSONValue in app.js when a change happens in JSON component. This should be applied as soon as the react app loads.
APP.JS:
import React, { Component } from 'react';
import Home from './Components/Home';
import './App.css';
class App extends Component
{
constructor(props) {
super(props);
this.state = {
JSONValues: {
JSONValue: ""
}
}
this.changeJSON = this.changeJSON.bind(this);
changeJSON(e, JSONId)
{
const elem = e.target;
const JSONValue = elem.value;
let tmpJSONValues = this.state.JSONValues;
tmpJSONValues[JSONId] = JSONValue;
this.setState(prevState => ({
JSONValues: tmpJSONValues
}));
}
render() {
return (
<div>
<Home
JSONValues={this.state.JSONValues}
changeJSON={this.changeJSON}
/>
</div>
)
}
}
export default App;
HOME.JS:
returns:
<div>
<JSON
JSONValues={props.JSONValues}
changeJSON={props.changeJSON}
/>
</div>
JSON.JS:
import React, { Component } from 'react';
export default class JSON extends Component {
render() {
let JSONValue = "";
if(this.props.JSONValues){
JSONValue = this.props.JSONValues.JSONValue;
}
JSONValue = 'value';
return (
<div>
<textarea className="json" spellCheck={false} value={JSONValue} onChange={(e) =>this.props.changeJSON(e, 'JSONValue')}>
</textarea>
</div>
)
}
}
Normally, you create state in the parent component and then pass the data to the child through props.
In the parent, you also create a changeStateHandler that you also pass to the child component through props.
When the child needs to update its state, it uses the passed in changeStateHandler. The parent then updates its state and passes the new state back to the child through props. The child rerenders when it gets the new props.
This is a simple contrived example:
Demo.js
import React, {useState} from 'react';
export const Parent = () => {
const [counter, setCounter] = useState(0);
return (
<div>
<div>Parent says the count is now: {counter}</div>
<Child counter={counter} handleCounter={setCounter} />
</div>
);
};
const Child = ({counter, handleCounter}) => {
const incrementCounter = () => handleCounter(counter++);
const decrementCounter = () => handleCounter(counter--);
return (
<div>
<button onClick={incrementCounter}>+</button>
<button onClick={decrementCounter}>-</button>
</div>
);
};
Pass a function through child props to set the parent state on mount as below:
class Child extends React.Component {
componentDidMount(){
const jsonString = '{"foo":"bar"}';
this.props.onMount(jsonString)
}
render() {
return null;
}
}
class App extends React.Component {
state = {
jsonData: null
}
setJson = jsonData => this.setState({ jsonData });
render(){
return (
<div>
<span>parent's state jsonData:</span>
<pre>{this.state.jsonData}</pre>
<Child onMount={this.setJson} />
</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>
I am getting map undefined when i am sending props Two times as separate components
import React, { Component } from 'react'
import Todo from './Todo';
export default class App extends Component {
state = {
todos: [
{id : 1 , content: "lets sleep"},
{id: 2, content:"lets eat "}
]}
deletTodo = (id) => {
console.log(id)
}
render() {
return (
<div className="App container">
<h1 className="center blue-text">Todo's</h1>
<Todo todo = {this.state.todos} />
{ <Todo deletTodo = {this.deletTodo}/> }
</div>
)
}
}
It is throwing me map of undefined but the following code does the trick i don't know why any one explain
<Todo todo = {this.state.todos} deletTodo= {this.deletTodo}/>
The following is my Todo.js where i am getting the props
import React, { Component } from 'react'
export default class Todo extends Component {
render() {
return (
<div className= "todos collection">
{
this.props.todo.map((td)=>{
return (
<div className="collection-item" key ={td.id} >
<span>{td.content}</span>
</div>
)})}
</div>
)
}
}
Both the usage of component will create seperate instances. Only the props that you provide in that instance will be available as this.props.
in <Todo todo = {this.state.todos} /> only todo prop is available and deletTodo is not available. In { <Todo deletTodo = {this.deletTodo}/> } only deletTodo is available and todos prop is not available. This is the reason you will get the error Cannot read property 'map' of undefined. You can fix this by providing a default prop so that none of the props are ever undefined.
Todo.defaultProps = {
todo: [],
deletTodo: () => null,
}
Set your state in a constructor
constructor() {
super();
this.state = {
//set state here
}
I am learning React and I am trying to call a function in a child component, that accesses a property that was passed from parent component and display it.
The props receives a "todo" object that has 2 properties, one of them is text.
I have tried to display the text directly without a function, like {this.props.todo.text} but it does not appear. I also tried like the code shows, by calling a function that returns the text.
This is my App.js
import React, { Component } from "react";
import NavBar from "./components/NavBar";
import "./App.css";
import TodoList from "./components/todoList";
import TodoElement from "./components/todoElement";
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
};
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo(input) {
const newTodo = {
text: input,
done: false
};
const todos = [...this.state.todos];
todos.push(newTodo);
this.setState({ todos });
}
render() {
return (
<div className="App">
<input type="text" id="text" />
<button
onClick={() => this.addNewTodo(document.getElementById("text"))}
>
Add new
</button>
{this.state.todos.map(todo => (
<TodoElement key={todo.text} todo={todo} />
))}
</div>
);
}
}
export default App;
This is my todoElement.jsx
import React, { Component } from "react";
class TodoElement extends Component {
state = {};
writeText() {
const texto = this.props.todo.text;
return texto;
}
render() {
return (
<div className="row">
<input type="checkbox" />
<p id={this.writeText()>{this.writeText()}</p>
<button>x</button>
</div>
);
}
}
export default TodoElement;
I expect that when I write in the input box, and press add, it will display the text.
From documentation
Refs provide a way to access DOM nodes or React elements created in the render method.
I'll write it as:
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
};
this.textRef = React.createRef();
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo() {
const newTodo = {
text: this.textRef.current.value,
done: false
};
const todos = [...this.state.todos, newTodo];
this.setState({ todos });
}
render() {
return (
<div className="App">
<input type="text" id="text" ref={this.textRef} />
<button onClick={this.addNewTodo}>Add new</button>
{this.state.todos.map(todo => (
<TodoElement key={todo.text} todo={todo} />
))}
</div>
);
}
}
In your approach, what you got as an argument to the parameter input of the method addNewTodo is an Element object. It is not the value you entered into the text field. To get the value, you need to call input.value. But this is approach is not we encourage in React, rather we use Ref when need to access the html native dom.
I have a component in my app that renders some data, most commonly a page title.
Markup:
<Toolbar>
<ToolbarRow>
<div id="title-bar">
{children}
</div>
</ToolbarRow>
</Toolbar>
How would I declaratively be able to change the data inside?
I've tried react-side-effects which allowed me to indeed change the title to be rendered but then I wanted to be able to add components as well.
Components aren't to be stored inside state so there's that…
Then I looked at Portals, which seem to exactly what I want but I get Target container is not a DOM element.
Markup for the portal component:
import React from "react";
import {createPortal} from 'react-dom'
const PageTitle = ({title, children}) => {
return createPortal(
<p>foo</p>,
document.getElementById('title-bar')
)
};
export default PageTitle;
I'm calling the portal component like so:
<PageTitle title="Document Overview"/>
As you can see from the above snippet, the other component adds a <div id="title-bar" />, so I guess it has to do with timing.
Anyone have a good idea?
I would just put components into the state here:
const bars = [];
export class TitleBar extends Component {
state = { children: [] };
componentDidMount() { bars.push(this); }
componentWillUnmount() { bars.splice(bars.indexOf(this), 1); }
render() { return this.state.children };
}
const RealPageTitle = ({ title }) => <div> { title } </div>;
export class PageTitle extends Component {
constructor(props) {
super(props);
this.real = RealPageTitle(props);
}
componentDidMount() {
for(const bar of bars)
bar.setState(({ children }) => ({ children: children.concat(this.real) }));
}
componentWillUnmount() {
for(const bar of bars)
bar.setState(({ children }) => ({ children: children.filter(child => child !== this.real) }));
}
render() { }
}
That way you can just add <PageTitle title={"Test"} /> somewhere on the page and it gets added to the title bar.
I know this does not follow "best practices", but it certainly works