Component not re-rendering when dictionary value changes - javascript

Simple: I change a dictionary value and the component is not re-rendering. The value actually changes when I log it, it just doesn't render on the screen.
This is where it's happening. The Icon should change from 'caret-down' to 'caret-right' but for some reason it's not:
import React, {Component} from 'react';
import {inject,observer} from 'mobx-react';
#inject("appStore") #observer
class Attribute extends Component {
...
toggleValueDisplay = (attr) => {
node.attributeToggle[attr] = !node.attributeToggle[attr];
};
render() {
...
const { node, attr } = this.props;
let vals = node.attributes.get(attr);
return (
<div>
<span>
<div>{attr}</div>
<Icon type={node.attributeToggle[attr] ? "caret-down" : "caret-right"} onClick={(attr) => {this.toggleValueDisplay(attr)}}/>
</span>
...
</div>
)
}
}
export default Attribute;
This is where the Attribute component is being rendered:
import React, {Component} from 'react';
import {inject,observer} from 'mobx-react';
import Attribute from "./attribute";
#inject("appStore") #observer
class Tab extends Component {
...
render() {
let node = this.props.appStore.repo.canvas.currentNode;
return (
<div className="tab-body">
{/* ATTRIBUTES */}
{
<div>
<h5>Attributes</h5>
{
[...node.attributes.keys()].map((attr) => {
return <Attribute node={node} attr={attr} key={attr}/>
})
}
</div>
}
</div>
);
}
}
export default Tab;
This is the Node object, for reference
import {observable} from 'mobx';
export default class Node {
id = '';
...
#observable attributes = new Map(); // {attribute : [values]}
#observable attributeToggle = {}; // {attribute : bool}
constructor(r) {
for (let property in r) {
this.attributes.set(property, r[property]);
this.attributeToggle[property] = false;
}
}
}
========================= THINGS I HAVE TRIED =========================
I've tried changing this:
{
node.attributeToggle[attr] ?
<Icon type="caret-down" onClick={(attr) => {this.toggleValueDisplay(attr)}}/>
:
<Icon type="caret-down" onClick={(attr) => {this.toggleValueDisplay(attr)}}/>
}
and also this where the Attribute component is used in Tab
{/* ATTRIBUTES */}
{
<div>
<h5 >Attributes</h5>
{
[...node.attributes.keys()].map((attr) => {
return <Attribute node={this.props.appStore.repo.canvas.currentNode} attr={attr} key={attr}/>
})
}
</div>
}
but it doesn't work. Not sure why this isn't working please help :)

You are using props which are not mutable use state instead

You don't trigger the component render anywhere, so of course it doesn't update. You can do it manually like this:
toggleValueDisplay = (attr) => {
node.attributeToggle[attr] = !node.attributeToggle[attr];
this.forceUpdate()
};

Related

Why isn't the component child rendered?

I have written the following code in APP.js component:
import React from "react";
import Exam from "./exam.js";
export default function App() {
return (
<Exam>
<h1>hashemi</h1>
</Exam>
);
}
And I have written the following code in exam.js component:
import React from "react";
const Exam = ({child}) => {
return (
<div>
<p>parastoo</p>
{child}
</div>
);
};
export default Exam;
But the output shows this:
parastoo
What is the problem? Why doesn't the child <h1> render?
Child components are passes via the children prop to the component, even if there is only a single child:
const Exam = ({children}) => {
return (
<div>
<p>parastoo</p>
{children}
</div>
);
};
It's called props.children. Read from the documentation section Containment.
const Exam = (props) => {
return (
<div>
<p>parastoo</p>
{props.children}
</div>
);
};
I hope this helps!
In React, you can pass props, or properties, to child components. Say you have an App component which renders a child component called CurrentDate which is a stateless functional component. You can pass CurrentDate a date property by writing:
const CurrentDate = (props) => {
return (
<div>
{ /* change code below this line */ }
<p>The current date is: {props.date} </p>
{ /* change code above this line */ }
</div>
);
};
Calender is a parent Component, you can pass Calender a date property by writing
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* change code below this line */ }
<CurrentDate date={Date()}/>
{ /* change code above this line */ }
</div>
);
}
};

How to read props on event on React

I creating chat system by React and Firebase.
The data of chat stystem is managemented by Firebase RealTimeDatabase.
Now site here
URL: https://react-chat-b0e8a.firebaseapp.com/
Github: https://github.com/kaibara/React-chat
I'm trying to implement the delete button, but I do not know how to make the child component event read the parent componentthis.props.
As a solution to this, I was thinking to have this.props read in front of render.
But I do not know how to do it.
Can you share the solution to this problem in the following code?
App.js - parenet component
import React, { Component } from 'react'
import firebase from 'firebase/app'
import { firebaseApp,firebaseDB } from './firebase/firebase'
import ChatMessage from './components/ChatMessage'
const messagesRef = firebaseDB.ref('messages')
class App extends Component {
constructor(props) {
super(props)
this.state = {
text : "",
user_name: "",
messages: []
}
}
componentWillMount() {
messagesRef.on('child_added', (snapshot) => {
const m = snapshot.val()
let msgs = this.state.messages
msgs.push({
'text' : m.text,
'user_name' : m.user_name,
'key': snapshot.key
})
console.log({msgs})
this.setState({
messages : msgs
})
console.log(this.state.messages)
})
}
render() {
return (
<div className="App">
<div className="MessageList">
<h2>メッセージログ</h2>
{this.state.messages.map((m, i) => {
return <ChatMessage key={i} messages={m} />
})}
</div>
</div>
)
}
}
export default App
ChatMessage.js - child component
import React,{Component} from 'react'
import { firebaseDB } from '../firebase/firebase'
const messagesRef = firebaseDB.ref('messages')
class ChatMessage extends Component {
onRemoveClick(){
messagesRef.child(this.props.messages.key).remove()
// I want to load `this.props.messages.key` here
}
render(){
return(
<div className="Message">
<p>{this.props.messages.key}</p>
<p className="MessageText">{this.props.messages.text}</p>
<p className="MessageName" style={user}>by {this.props.messages.user_name}</p>
<button className="MessageRemove" onClick={this.onRemoveClick}>削除</button>
</div>
)
}
}
export default ChatMessage
Please lend me your knowledge.
Thank you.
Implement the handler in your parent component and pass the reference down to child component has props
implement onRemoveClick() in App component and pass the handler refrence in `props' to ChatMessage component.
App component:
deleteMessageHandler = (key) =>{
const messages = [...this.state.messages];
messages = messages.splice(key,1);
this.setState({messages:messages});
}
ChatMessage:
render() {
return (
<div className="App">
<div className="MessageList">
{this.state.messages.map((m, i) => {
return <ChatMessage key={i} messages={m} deleteMessageHandler={this.deleteMessageHandler}/>
})}
</div>
</div>
)
}
Note: Don't use the index of the map has a key to the components in the map, its an antipattern, it should be proper unique id's.

React-Chat-Widget props not forwarded

I am using the react-chat-widget and trying to call a function in the base class of my application from a custom component rendered by the renderCustomComponent function of the widget.
Here is the code for the base class:
import React, { Component } from 'react';
import { Widget, handleNewUserMessage, addResponseMessage, addUserMessage, renderCustomComponent } from 'react-chat-widget';
import 'react-chat-widget/lib/styles.css';
import Reply from './Reply.js';
class App extends Component {
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, this.correct);
}
correct = () => {
console.log("success");
}
render() {
return (
<div className="App">
<Background />
<Widget
handleNewUserMessage={this.handleNewUserMessage}
/>
</div>
);
}
}
export default App;
And here is the code for the custom component Reply:
import React, { Component } from 'react';
import { Widget, addResponseMessage, renderCustomComponent, addUserMessage } from 'react-chat-widget';
class Reply extends Component {
constructor(props) {
super(props);
}
sendQuickReply = (reply) => {
console.log(this.props); //returns empty object
//this.props.correct(); <-- should be called
};
render() {
return (
<div className="message">
<div key="x" className={"response"}onClick={this.sendQuickReply.bind(this, "xx")}>xx</div>
</div>)
}
}
export default Reply;
According to ReactJS call parent method this should work. However, when I print the this.props object it is empty, although the documentation of the renderCustomComponent method states that the second argument of the component to render are the props that the component needs (in this case the parent class function).
Where have I gone wrong?
The second parameter is considered as props, but it is expected to be an object. you would pass it like
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, {correct: this.correct});
}

React Initial Null Object

I am passing down state from a parent component to a container to select a collection to return, however when the object is returned from the Create Container it is initially Null and so obviously I cannot access any of the properties.
EDIT: forgot to mention that although it is initially null it does then render the full object with an information alert that "the value below was evaluated just now"
I could understand if this was an ajax request but it isn't so I am stumped.
here is the code, can anyone enlighten me?
import React, {Component} from 'react';
import {createContainer} from 'meteor/react-meteor-data';
import Scenes from '../imports/collections/scene.js';
import SceneUpdateForm from '../imports/components/scene_update_form6.js';
//parent stateholder
export default class Home extends Component {
constructor() {
super();
this.state = {sceneState: 1};
}
incrementCount() {
this.setState({sceneState: this.state.sceneState + 1});
}
decrementCount() {
this.setState({sceneState: Math.max(1,this.state.sceneState - 1)}) }
render() {
return (
<Container
sceneState={this.state.sceneState}
incClick={this.incrementCount.bind(this)}
decClick={this.decrementCount.bind(this)}
/>
);
}
}
// Child component
function ChildComponent(props){
return (
<div>
<button onClick={props.decClick}> Dec </button>
<button onClick={props.incClick}> Inc </button>
<SceneUpdateForm
scene={props.scene}
/>
</div>
)
}
//container
let Container = createContainer((props) => {
let doc = Scenes.findOne({sheet_no: props.sceneState});
return {
scene: doc ? doc : null
}
}, ChildComponent);

How to send this.state from a component to relay root container

I want to change my root query parameter based on the this.state.eventid which is a child component, but I have no clue how to get props to relay root container. I started based on relay-starter-kit.
I have React component that has a dropdown menu, and onSelect it setStates for eventId
renderAttend() {
if (this.props.groups != null && this.state.success != true) {
var events = this.props.events.map(function(event){
var boundSelect = () => {this.setState({eventid:event.nodes[0].id})}
return <MenuItem style={{fontSize:20}}eventKey={event.nodes[0].id} onSelect={boundSelect.bind(this)}>{event.nodes[0].properties.summary} / {event.nodes[0].properties.start}</MenuItem>
},this)
var teams = this.props.groups.map(function(team){
var boundSelect = () => {this.setState({teamid:team.nodes[0].id})}
return <MenuItem style={{fontSize:20}}eventKey={team.nodes[0].id} onSelect={boundSelect.bind(this)}>{team.nodes[0].properties.name}</MenuItem>
},this)
return (
<div>
<ButtonGroup>
<DropdownButton style={{padding:"15px",fontSize:20}}title="Events" id="bg-vertical-dropdown-2">
{events}
</DropdownButton>
<DropdownButton style={{padding:"15px",fontSize:20,marginLeft:"5px"}} title="Groups" id="bg-vertical-dropdown-2">
{teams}
</DropdownButton>
</ButtonGroup>
</div>
)
}
}
I want to use this state to somehow change my root query...
my approute...
import Relay from 'react-relay';
export default class extends Relay.Route {
static paramDefinitions = {
eventId: {required: false}
};
static queries = {
Event : () => Relay.QL`query{eventState(eventId:$eventId)}`,
};
static routeName = 'AppHomeRoute';
}
and my app.js
import 'babel-polyfill';
import App from './components/App';
import AppHomeRoute from './routes/AppHomeRoute';
import React from 'react';
import ReactDOM from 'react-dom';
import Relay from 'react-relay';
ReactDOM.render(
<Relay.RootContainer
Component={App}
route= {new AppHomeRoute}
renderLoading={function() {
return <div style= {{display:"flex",justifyContent:"center",marginTop:"55px"}}> <h1>Loading...</h1></div>;
}}
renderFailure={function(error, retry) {
return (
<div>
<h1>Click Refresh</h1>
</div>
);
}}
/>,
document.getElementById('root')
);
Now I want to this.state.eventid from the react component to update my root query, but I have no idea how to pass data from child component to react root.container. I do not want to use react-router for this :)
p.s. this.props.events were passed to me by an ajax call so they are not saved in relay/graphql data.
For such a case, the better thing to do is to wrap your root query into a story like
{
store {
events(eventId:$eventId)
}
}
So in the root query you only have
export default class extends Route {
static queries = {
app:() => Relay.QL`query { store }`
};
static routeName = "AppRoute";
}
And in the page you create a fragemnt like
let RelayApp = createContainer(SomeComponent, {
initialVariables: {
eventId: null
},
fragments: {
app: () => Relay.QL `
fragment on Store {
id
events(eventId: $eventId) {
pageInfo {
hasNextPage
}
edges {
cursor
node {
name
...
}
}
}
}
`,
},
});
export
default RelayApp;
For the child component, you set the eventId and onChange event handler as props from parent component. And in the parent componet you implement the event handler and call this.props.setVariables({eventId: someVal}) like
// Child Component
export default class Menu extends Component {
render() {
return(
<ul>
<li onClick={() => this.props.selectItem(val)}>{val}</li>
...
</ul>
)
}
}
// Parent Component
class Main extends Component {
_selectItem = (val) => {
this.props.relay.setVariables({eventId: val});
}
render() {
return(
<div>
<Menu selectItem={() => this._selectItem}/>
</div>
)
}
}
let RelayApp = ...
export default Main
Hope this will help.
There is no easy way to solve this. Either use react-router-relay or nest your query like this and use this.props.relay.setVariables()
viewer {
eventState(eventid:$eventid) {
data
}
}

Categories

Resources