In component BlocklyDrawer i'm trying to change the code state of the parent component. I do it in the onChange() event, calling the method of the parent component handleCodex:
constructor(props) {
super(props);
this.state = {
code : "xxx",
};
this.handleCodex =
this.handleCodex.bind(this);
}
handleCodex(codex){
this.setState = ({
code: codex,
});
}
<BlocklyDrawer
tools={[INICIAR, MOVER, ATACAR]}
language = {Blockly.Javascript}
onChange={(code, workspace) => {
this.handleCodex(code);
}}
appearance={
{
categories: {
Agente: {
colour: '160'
},
},
}
}
>
Although the method handleCodex is executed, the code state does not change.
constructor(props) {
super(props);
this.state = {
code : "xxx",
};
this.handleCodex =
this.handleCodex.bind(this);
}
handleCodex(codex){
this.setState({
code: codex,
}); // this.setState is like a function call. Not a assignment statement.
}
<BlocklyDrawer
tools={[INICIAR, MOVER, ATACAR]}
language = {Blockly.Javascript}
onChange={(code, workspace) => {
this.handleCodex(code);
}}
appearance={
{
categories: {
Agente: {
colour: '160'
},
},
}
}
>
Related
How can I remove arrays from the render that, in fact, should not be there (commitsContentValues, membersContentValues, commitsValue, memberValue) and move it to separate methods. How can I make my code cleaner? And in which chapter of the reсt documentation is it described?
export default class Content extends Component {
constructor(props) {
super(props);
this.state = {
iteams: {},
};
}
async componentDidMount() {
membersData = 123
commitsData =zxc
this.setState({
iteams: { membersData, commitsData },
});
} catch {
console.log(err);
}
render() {
const { iteams } = this.state;
const commitsContentValues = { //this
firstRowText: "Date",
secondRowText: "Author",
thirdRowtext: "Commit Message",
firstRowKey: "committed_date",
secondRowKey: "author_name",
thirdRowkey: "title",
};
const membersContentValues = { //this
firstRowText: "Username",
secondRowText: "Name",
thirdRowtext: "GitLab Id",
firstRowKey: "username",
secondRowKey: "name",
thirdRowkey: "id",
};
const commitsValue = { //this
dataKey: "commitsData",
arrKey: "commits",
};
const memberValue = { //this
dataKey: "membersData",
arrKey: "members",
};
return (
<div>
{this.props.idx === 0 && (
<BasicTable
commits={iteams.commitsData}
commitsData={commitsContentValues}
keys={commitsValue}
></BasicTable>
...
The easiest solution is to just have the values you want returned from a function as you said:
export default class Content extends Component {
constructor(props) {
super(props);
this.state = {
iteams: {},
};
}
function getCommitsContentValues() {
return {
firstRowText: "Date",
secondRowText: "Author",
thirdRowtext: "Commit Message",
firstRowKey: "committed_date",
secondRowKey: "author_name",
thirdRowkey: "title",
};
}
render() {
return (
<div>
<BasicTable
commitsData={this.getCommitsContentValues()}
></BasicTable>
</div>
)
}
}
Is this what you are trying to do?
function typeContactGetter is binded to this and everything is working, the only issue is in the functions return on the <li> element, I am trying to set a className coming from state and it returns undefined for this.state.
Why is this happening?
Thanks,
Bud
component
class ContactType extends Component {
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
familyContacts: this.typeContactGetter("Family"),
friendContacts: this.typeContactGetter("Friends")
};
this.typeContactGetter = this.typeContactGetter.bind(this);
this.handleClick = this.handleClick.bind(this);
this.hideList = this.hideList.bind(this);
}
handleClick = (event) => {
event.preventDefault();
console.log('clicked, state: ' + this.state.hiddenList);
};
hideList = () => {
console.log("this is hidelist: " + this.state.hiddenList);
if (this.state.hiddenList === true){
this.setState({
hiddenList: false
});
}
this.setState({
hiddenList: !this.state.hiddenList
});
};
typeContactGetter = (name) => {
console.log(this.state);
for (let contact of CONTACTS) {
if (contact.name === name) {
return (
<li className={this.state.hiddenList ? 'hidden' : ''} onClick={this.handleClick} key={contact.id.toString()}>
{contact.contacts.map(value => {
if (value.type === "Contact") {
return (
<a key={value.id.toString()} href="#">{value.name}</a>
);
}
})
}
</li>
);
}
}
};
render() {
return (
<ContactView familyContacts={this.state.familyContacts} friendContacts={this.state.friendContacts} hideList={this.hideList}/>
);
}
}
export default ContactType;
That's because you call typeContactGetter in the constructor before the state is actually created.
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
familyContacts: this.typeContactGetter("Family"), // hey, but we are actually creating the state right now
friendContacts: this.typeContactGetter("Friends")
};
}
Why do you want to keep a component list in the state? Maybe it is better to pass them directly:
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
};
}
....
<ContactView familyContacts={this.typeContactGetter("Family")} friendContacts={this.typeContactGetter("Friends")} hideList={this.hideList}/>
btw you don't need to bind function as they are bound already by arrow functions.
I'm trying to persist data with re-base but with this setup, I'm getting nothing. When I try to addExpense I'm getting no errors and no new object.
My re-base setup here:
class ExpensesContainer extends Component {
constructor(props) {
super(props);
this.state = {
expenses: {
expenseList: []
}
}
}
componentDidMount(){
base.syncState(`/`, {
context: this,
state: 'expenses.expenseList',
asArray: true
});
}
addExpense = (expense) => {
this.setState({
expenses: this.state.expenses.expenseList.concat([expense]) //updates Firebase and the local state
});
}
If I'm not using componentDidMount then my addExpense action looks like this and everything works well:
// addExpense = (expense) => {
// const expenseList = [...this.state.expenses.expenseList, expense];
// this.setState({expenses: {expenseList}});
// }
expenses: this.state.expenses.expenseList.concat([expense])
on the .setState() method you're passing the wrong value, that in the end will look like { expenses: [<list here>] } instead should be:
expenses: { expenseList: this.state.expenses.expenseList.concat([expense])}
and re-base doesn't work only with .concat() (by reading the documentation I also though the same)
This said, you just need to .setState() the expected variable and it should update.
In case someone gets into a similar issue - this the solution that worked for me:
class ExpensesContainer extends Component {
constructor(props) {
super(props);
this.state = {
expenseList: []
}
}
componentDidMount(){
base.syncState(`/expenses`, {
context: this,
state: 'expenseList',
asArray: true
});
}
deleteExpense = (expenseToRemove) => {
const expenseList = [...this.state.expenseList].filter((expense) => {
return expenseToRemove !== expense.uid;
});
this.props.history.push("/expenses");
this.setState({expenseList});
}
addExpense = (expense) => {
this.setState({
expenseList: this.state.expenseList.concat([expense])
});
this.props.history.push("/");
}
This is my first week into React. I'm finding the implementation of trickle down state to be elusive.
What I would like to happen: When the parent container receives a message from the socket I want the state change of the parent to trickle down to each of the child elements.
What is currently happening: With the code below all the components render to the screen, the message is received, but the child elements do not register any type of update.
**Update: Everything works fine if I don't loop the ProgressBlocks into an array and then place that array in the render tree with {this.state.blocks}. This is unfortunate since it's not dynamic but at least there's progress.
Parent Container
class InstanceContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
id: ids.pop(),
callInProgress: false,
callsCompleted: 0,
eventProgress: 0,
blocks: []
}
}
componentWillMount() {
const lightColors = ['white', 'white', 'cyan', 'green', 'green', 'yellow', 'orange', 'magenta', 'red']
for (let i = 0; i < 10; i++) {
this.state.blocks.push(<ProgressBlock bgColor={lightColors[i]} eventPlace={i} eventProgress={this.state.eventProgress} />)
}
}
render() {
socket.on(this.state.id, (msg) => {
console.log(msg)
console.log('RECEIVED MESSAGE')
console.log(this.state.id)
if (msg) {
this.setState((prevState) => ({
eventProgress: 0,
callsCompleted: prevState.callsCompleted + 1
}))
} else {
this.setState((prevState) => ({
eventProgress: prevState.eventProgress + 1
}))
}
console.log(this.state.eventProgress)
})
return (
<div className="instance-container" id={this.state.id}>
{this.state.blocks}
<CompletionCounter callsCompleted={this.state.callsCompleted} />
<DisplayLog />
<VenueName />
</div>
)
}
}
Child Element
class ProgressBlock extends React.Component {
constructor(props) {
super(props)
this.state = {
light: false,
eventProgress: this.props.eventProgress,
bgColor: this.props.bgColor
}
}
componentWillUpdate() {
if (this.state.eventProgress >= this.props.eventPlace) {
this.setState({
light: true
})
}
}
render() {
console.log(this.state.eventProgress) // Does not log when parent changed
console.log(this.props.eventPlace) // Does not log when parent changed
const styleObj = {
backgroundColor: '#232323'
}
if (this.light) {
const styleObj = {
backgroundColor: this.props.bgColor
}
}
return <div className="progress-block" style={styleObj} />
}
}
The constructor in the child element is called once and hence your state is not gettinng updated in the child element. Also its an antipattern to set the state at the constructor when it depends on props. You should do it in the componentWillReceiveProps lifecycle function which is called when the props are updated. See the code below
class ProgressBlock extends React.Component {
constructor(props) {
super(props)
this.state = {
light: false,
eventProgress: '',
bgColor: ''
}
}
componentWillMount() {
this.setState({eventProgress: this.props.eventProgress, bgColor: this.props.bgColor});
}
componentWillReceiveProps(nextProps) {
this.setState({eventProgress: nextProps.eventProgress, bgColor: nextProps.bgColor});
}
componentWillUpdate() {
if (this.state.eventProgress >= this.props.eventPlace) {
this.setState({
light: true
})
}
}
render() {
console.log(this.state.eventProgress)
console.log(this.props.eventPlace)
const styleObj = {
backgroundColor: '#232323'
}
if (this.light) {
const styleObj = {
backgroundColor: this.props.bgColor
}
}
return <div className="progress-block" style={styleObj} />
}
}
so i have this in my code like this:
class TimersDashboard extends React.Component{
constructor() {
super(props);
this.state = {
timers: [
{id: uuid.v4(), text:'I am the first id' },
{ id:uuid.v4(), text:'I am the second text' }
]
};
}
clickEdit(id) {
this.openForm(id);
}
openForm(id) {
this.setState({
timers: this.state.timers.map((timer) => {
if(timer.id === id) {
return Object.assign({}, timer, { editFormOpen: true });
} else {
return timer;
}
})
});
}
handleCloseForm(id) {
this.closeForm(id);
}
closeForm(id) {
this.setState({
timers: this.state.timers.map((timer) => {
if(timer.id === id) {
return Object.assign({}, timer, { editFormOpen: false });
} else {
return timer;
}
})
});
}
}
render() {
return (
<Timer id={this.state.data[0].id} onEdit={this.clickEdit.bind(this)} onDelete = {this.handleCloseForm.bind(this)}/> // as if wroking on the first id
);
}
}
}
However, below, I passed the methods as props, the other component I tried to invoke these the same way, you can see their code is slightly similar in way.
class Timer extends React.Component {
constructor(props) {
super(props);
this.handleEditClick = this.handleEditClick.bind(this);
this.handleTrashClic = handleTrashClic.bind(this);
}
handleEditClick() {
this.props.onDelete(this.props.id);
}
handleTrashClick() {
this.props.onEdit(this.props.id);
}
render() {
return(
// ... onClick = {()=>this.handleEditClick(this.props.id)} ..
// ... onClick = {()=>this.handleTrashClick(this.props.id)} ..
);
}
}
}
I code them same way on other component, the delete method works on other component but I don't know why the Edit method does not and I can't make it work, I tried to pass the parentObj context, added .bind(this), But I cannot make it work. My error is "Warning: undefined(...): Cannot update during an existing state transition...". How do I make it work?
Created the same example in jsfiddle, its working. Try this:
Parent Component:
class TimersDashboard extends React.Component{
constructor(props) {
super(props);
this.state = {
timers: [
{id: 1, text:'I am the first text' },
{id: 2, text:'I am the second text' }
]
};
}
edit(id){
let timers = this.state.timers.map((timer) => {
if(timer.id === id) {
return Object.assign({}, timer, { editFormOpen: true });
} else {
return timer;
}
})
this.setState({timers});
}
remove(id){
let timers = this.state.timers.map((timer) => {
if(timer.id === id) {
return Object.assign({}, timer, { editFormOpen: false });
} else {
return timer;
}
})
this.setState({timers});
}
render() {
return (
<div>
<Timer id={1} edit={this.edit.bind(this)} remove={this.remove.bind(this)}/>
</div>
);
}
}
Child Component:
class Timer extends React.Component{
constructor(props) {
super(props);
this.state={};
}
edit(id){
this.props.edit(id);
}
remove(id){
this.props.remove(id);
}
render(){
return(
<div>
In Child Component:
<br/>
Id: {this.props.id}
<p onClick={this.edit.bind(this,this.props.id)}>Edit</p>
<p onClick={this.remove.bind(this,this.props.id)}>Remove</p>
*click on edit and remove to change the state
</div>
)
}
}
Check jsfiddle for working example: https://jsfiddle.net/wqkfqusk/