Logging values in React - javascript

I have 3 components. App.js - Main. localLog.jsx stateless, LoadBoard.jsx statefull. I want to Take string of data from LoadBoard and display it in localLog.jsx. The problem is that I can't figure it out why LocalLog is not displaying on screen.
console.log(this.data.Array) in App.jsx localLog is ["configuration"]
(2) ["configuration", "It's good configuration"]
App.jsx
class App extends Component {
constructor(props) {
super(props);
this.dataArray = [];
this.state = {
headers: []
};
this.localLog = this.localLog.bind(this);
}
localLog(data) {
if (data) {
this.dataArray.push(data);
console.log(this.dataArray);
this.dataArray.map(data => {
return <LocalLog info={data} />;
});
}
}
render() {
return (
<>
<LoadBoard apiBase={this.state.apiBase} localLog={this.localLog} />
<pre id="log_box">{this.localLog()}</pre>
</>
);
}
}
localLog.jsx
let localLog = props => {
return (
<pre className={classes.background}>
<ul className={classes.ul}>
<li>{props.info}</li>
<li>hello world</li>
</ul>
</pre>
);
};
export default localLog;
LoadBoard.jsx
class LoadBoard extends Component {
constructor(props) {
super(props);
this.state = {
positionToId: []
};
}
componentDidMount() {
this.props.localLog("configuration");
this.props.localLog(`It's good configuration`);
}
render() {
return (
<div>
<h1>Nothing interesting</h1>
</div>
);
}
}

You are not returning anything from the localLog method, should be:
return this.dataArray.map(data => {
return <LocalLog info={data} />;
});
EDIT:
here is what your App component should look like.
class App extends Component {
constructor(props) {
super(props);
this.state = {
headers: [],
logs: []
};
this.addLog = this.addLog.bind(this);
}
// Add log to state
addLog(log) {
this.setState(state => ({
...state,
logs: [...state.logs, log]
}));
}
render() {
return (
<>
<LoadBoard apiBase={this.state.apiBase} localLog={this.addLog} />
<pre id="log_box">
{this.state.logs.map(log => {
return <LocalLog info={log} />;
})}
</pre>
</>
);
}
}

you should use setState method in order to re-render the component.
you can try this.
class App extends Component {
constructor(props) {
super(props);
this.state = {
headers: [],
dataArray: []
};
this.localLog = this.localLog.bind(this);
}
localLog(data) {
if (data) {
this.state.dataArray.push(data);
this.setState({dataArray: this.state.dataArray})
}
}
render() {
return (
<>
<LoadBoard apiBase={this.state.apiBase} localLog={this.localLog} />
<pre id="log_box">{this.state.dataArray.map(i => <LoaclLog info={i}/>)}</pre>
</>
);
}
}

Related

Why isn't React re-rendering the page after the state is changed?

so I was working on a basic Todo app using React.js and I was wondering why the todo component does not automatically re-render once the state changed (the state contains the list of todos- so adding a new todo would update this array)? It is supposed to re-render the Header and the Todo component of the page with the updated array of todos passed in as props. Here is my code:
import React from 'react';
import './App.css';
class Header extends React.Component {
render() {
let numTodos = this.props.todos.length;
return <h1>{`You have ${numTodos} todos`}</h1>
}
}
class Todos extends React.Component {
render() {
return (
<ul>
{
this.props.todos.map((todo, index) => {
return (<Todo index={index} todo={todo} />)
})
}
</ul>
)
}
}
class Todo extends React.Component {
render() {
return <li key={this.props.index}>{this.props.todo}</li>
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.addnewTodo = this.addnewTodo.bind(this);
}
addnewTodo = () => {
let inputBox = document.getElementById("input-box");
if (inputBox.value === '') {
return;
}
this.props.handleAdd(inputBox.value);
}
render() {
return (
<div>
<input id="input-box" type="text"></input>
<button type="submit" onClick={this.addnewTodo}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { todos: ['task 1', 'task 2', 'task 3']}
this.handleNewTodo = this.handleNewTodo.bind(this);
}
handleNewTodo(todo) {
let tempList = this.state.todos;
tempList.push(todo);
this.setState = { todos: tempList };
}
render() {
return (
<div>
<Header todos={this.state.todos} />
<Todos todos={this.state.todos} />
<Form todos={this.state.todos} handleAdd={this.handleNewTodo} />
</div>
)
}
}
You are not updating the state correctly.
You need to make a copy of the this.state.todos, add the new todo in the copied array and then call this.setState function
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
Notice that this.setState is a function
You're updating state incorrectly,
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
This is the correct syntax.

React JS : grandparent component's setState method doesn't update state of a grandchild input field onChange event click

In the same code, I was able to the get the grandparent component's setState method to update accordingly for an onClick event from the grandchild component, however, for the onChange event, it is failing. I am not getting any errors.
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber=()=> {
this.props.changeNumber();//call child method
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return(
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value = {data} onChange={this.handleChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return(
<div>
<GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.handleChange}/>
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1,
data: ""
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(data) {
this.setState({data:this.state.data});
console.log(data);
}
changeNumber=()=>{
this.setState((prevState)=>{
console.log(prevState,this.state.data);
return {
number : prevState.number + 1
}
});
}
render() {
const data = this.state.data;
const input = data;
return (
<Child number={this.state.number}
changeNumber = {this.changeNumber}
data={input}
onChange = {this.handleChange}
/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Console Result:
Object {
data: "",
number: 1
} ""
result screenshot:
console.log result
see code pen for live code:
https://codepen.io/codehorse/pen/yLyEwBw?editors=0011
Your improved code with live demo https://codesandbox.io/s/laughing-sky-kk97b
What need to change <GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.onChange}/>
Complete Code
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber = () => {
this.props.changeNumber(); //call child method
};
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return (
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value={data} onChange={this.props.onChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild
number={this.props.number}
changeNumber={this.props.changeNumber}
value={this.props.data}
onChange={this.props.onChange}
/>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 1,
data: ""
};
}
handleChange = e => {
this.setState({ data: e.target.value });
console.log(e.target.value);
};
changeNumber = () => {
this.setState(prevState => {
console.log(prevState, this.state.data);
return {
number: prevState.number + 1
};
});
};
render() {
const data = this.state.data;
const input = data;
return (
<Child
number={this.state.number}
changeNumber={this.changeNumber}
data={input}
onChange={this.handleChange}
/>
);
}
}
export default App;

ReactJS component update on array update

I have a basic 'wrapper' component which contains child 'item' components
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
render() {
return (
<div>Items count- {this.state.items.length}
{this.state.items.map(function (item, i) {
<Item itemId={item.itemId} />
})}
</div>
);
}
}
class Item extends React.Component {
constructor(props) { super(props); }
render() {
return (
<div class="item">{this.props.itemId}</div>
);
}
}
Do I call setState({ "items":[{ "itemId": 22 }] }); to update items in UI?
Want to add/remove 'item' and get UI updated accordingly.
For updates, you want to do something like the following...
// Update item
this.setState({ "items":this.state.items.map(function(item) {
if (item.itemId !== 22) return item;
// update item here
// remember to return item
return item
})
});
// Remove item
this.setState({ "items":this.state.items.filter(item => {
return item.itemId !== 22
})
})
// Add item
this.setState({ "items": this.state.items.concat(newItem)
})
I suggest putting these into React class methods though.
import React from 'react';
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this)
this.removeItem = this.removeItem.bind(this)
this.updateItem = this.updateItem.bind(this)
}
addItem (item) {
this.setState({
items: this.state.items.concat(item)
})
}
updateItem(id, updatedItem) {
this.setState({
items: this.state.items.map(function (item) {
if (item.itemId !== id) return item;
return updatedItem;
})
})
}
removeItem(id) {
this.setState({
items: this.state.items.filter(function(item) {
return item.itemId !== id
})
})
}
render() {
return (
<div>Items count- {this.state.items.length}
{this.state.items.map(function (item, i) {
<Item itemId={item.itemId} />
})}
</div>
);
}
}
class Item extends React.Component {
constructor(props) { super(props); }
render() {
return (
<div class="item">{this.props.itemId}</div>
);
}
}
State is not mutable, so the code you've shown there will replace items with an array with one object. If you'd like to add/remove from the array, you'll first need to copy the array somehow , and replace with the new one. You should use the function argument of setState for that. Ex:
this.setState(function (currentState) {
return {items: currentState.concat({itemId: 22})}
});
This is how you add and remove to and from the state items array:
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
addItems = (id) => {
// copies all current items and adds a new one
this.setState({items: [...this.state.items, {itemId: id}]
})
}
removeItems = (id) => {
const newItemList = this.state.items.filter((item) => item.itemId !== id)
this.setState({items: newItemList
})
}
render() {
return (
<div>
<div>Items count - {this.state.items.length}
<button onClick={() => this.addItems(this.state.items.length + 1)}>Add Item</button>
</div>
{
this.state.items.map((item) => {
return (
<Item key={item.itemId} itemId={item.itemId} removeItems={this.removeItems} />
)
})
}
</div>
);
}
}
ReactDOM.render(<Wrapper />, document.getElementById('root'))
class Item extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className="item">test{this.props.itemId} <button onClick={() => this.props.removeItems(this.props.itemId)}>Remove Item</button></div>;
}
}
<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>

How to change Parent component's state from Child component if it's rendering with map()

i have a problem - i need to change parent component's state from child component. I tried standard variant with props, but it's not helping in my case.
Here is code of Parent component:
class ThemesBlock extends React.Component {
constructor(props){
super(props);
this.state = {
currentThemeId: 0
}
}
changeState(n){
this.setState({currentThemeId: n})
}
render() {
let { data } = this.props;
return (
data.map(function (item) {
return <Theme key={item.id} data={item} changeState=
{item.changeState}/>
})
)
}
}
And here is my code for Child Component:
class Theme extends React.Component {
constructor(props){
super(props);
this.changeState = this.props.changeState.bind(this);
}
render() {
const { id, themename } = this.props.data;
const link = '#themespeakers' + id;
return (
<li><a href={link} onClick={() => this.changeState(id)}
className="theme">{themename}</a></li>
)
}
}
The primary issue is that changeState should be bound to the ThemesBlock instance, not to the Theme instance (by ThemesBlock).
Here's an example where I've bound it in the ThemesBlock constructor (I've also updated it to show what theme ID is selected):
class ThemesBlock extends React.Component {
constructor(props) {
super(props);
this.state = {
currentThemeId: 0
}
this.changeState = this.changeState.bind(this);
}
changeState(n) {
this.setState({currentThemeId: n})
}
render() {
let { data } = this.props;
return (
<div>
<div>
Current theme ID: {this.state.currentThemeId}
</div>
{data.map(item => {
return <Theme key={item.id} data={item} changeState={this.changeState} />
})}
</div>
)
}
}
class Theme extends React.Component {
constructor(props) {
super(props);
this.changeState = this.props.changeState.bind(this);
}
render() {
const {
data: {
id,
themename
},
changeState
} = this.props;
const link = '#themespeakers' + id;
return (
<li>{themename}</li>
)
}
}
const data = [
{id: 1, themename: "One"},
{id: 2, themename: "Two"},
{id: 3, themename: "Three"}
];
ReactDOM.render(
<ThemesBlock data={data} />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
Did you try to move map() before return? Like this:
render() {
let { data } = this.props;
const outputTheme = data.map(function (item) {
return <Theme key={item.id} data={item} changeState= {item.changeState}/>
})
return (
{outputTheme}
)
}

React Fetch Request

I was trying to change the site to which I do the fetch request when I click the button Next in the App component, passing the other site
<FilterableProductTable getSite={ this.state.active ? '/get_platfo
rms' : '/get_features' } />
but it is not working, it shows just the old information. I think is some asynchronous problem
export class FilterableProductTable extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
fetch() {
axios.get(this.props.getSite)
.then(res => {
this.setState({
posts: res.data.functionality
});
});
}
componentDidMount() {
this.fetch();
setTimeout(function(){this.fetch();} , 5000);
}
render() {
return (
<div>
<ProductTable products={this.state.posts} />
</div>
);
}
}
export class App extends React.Component {
constructor(props){
super(props);
this.state= {active: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
active: !prevState.active
}));
}
render() {
return (
<div>
<FilterableProductTable getSite={ this.state.active ? '/get_platforms' : '/get_features' } />
<a className={ this.state.active ? 'button' : 'hidden' } onClick={this.handleClick}><span>Next</span></a>
</div>
);
}
}
try this code:
export class FilterableProductTable extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
fetch() {
axios.get(this.props.getSite)
.then(res => {
this.setState({
posts: res.data.functionality
});
});
}
componentDidUpdate(){
this.fetch();
}
componentDidMount() {
this.fetch();
setTimeout(function(){this.fetch();} , 5000);
}
render() {
return (
<div>
<ProductTable products={this.state.posts} />
</div>
);
}
}
export class App extends React.Component {
constructor(props){
super(props);
this.state= {active: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
let newState = !this.state.active;
this.setState({
active: newState
});
}
render() {
return (
<div>
<FilterableProductTable getSite={ this.state.active ? '/get_platforms' : '/get_features' } />
<a className={ this.state.active ? 'button' : 'hidden' } onClick={this.handleClick}><span>Next</span></a>
</div>
);
}
}
Changes from your version: caught componentDidUpdate event(if the component updated fetch is called) and changed the handleClick method. Let me know if this fixes your problem.

Categories