flux: view doesn't refresh after store update - javascript

I study FLUX and try to write simple example on it. It is about a menu where I can select every single item (it is marked by (*)). So, here is main excerpts of the code:
AppContainer.js:
class AppContainer extends React.Component
{
static getStores() {
return [MenuStore];
}
static calculateState(prevState) {
return {
menu: MenuStore.getState(),
onclick: Actions.onclick
};
}
render() {
return <MenuView onclick={this.state.onclick} menu={this.state.menu.get('list')} cur={this.state.menu.get('cur')} />;
}
}
MenuStore.js
class MenuStore extends ReduceStore{
constructor() {
super(MenuDispatcher);
}
getInitialState() {
// return {list: ["About", "Contacts", "Price", "Home"], cur: 0};
return Immutable.Map({list: ["About", "Contacts", "Price", "Home"], cur: 2});
}
reduce(state, action) {
switch (action.type) {
case ActionTypes.ON_CLICK:
console.log('MenuStone ON_CLICK');
state.cur = action.index;
return state;
default:
return state;
}
}
}
MenuView.js:
class MenuView extends React.Component{
constructor(props){
super(props);
}
render(){
return <ui>{this.props.menu.map((menu, index) => {
var active = (this.props.cur === index) ? true : false;
return <MenuItem onclick={this.props.onclick} key={index} index={index} active={active} label={menu} />;
})}</ui>;
}
}
class MenuItem extends React.Component {
constructor(props) {
super(props);
this.state = {label: props.label};
this.click = this.click.bind(this);
}
click(index) {
console.log('MenuItem click');
this.props.onclick(this.props.index);
}
render() {
var mark = '';
if (this.props.active)
mark = '(*) ';
return <li onClick={this.click}>{mark}{this.state.label}</li>;
}
}
Actions.js:
const Actions = {
onclick(index) {
console.log('Actions.onclick');
MenuDispatcher.dispatch({
type: ActionTypes.ON_CLICK,
index,
});
}
};
When I run the code, it is work fine until I click a menu item. The code
state.cur = action.index;
executes, but nothing changes on the page.
What should I do to update the views?

Related

Why setState() doesn't update render on React?

I am trying to make a 2D game-board that consists of "Cells" which has a binary state value called isAlive. The user can change this value by clicking on them. This value is indicated to the user using different colors. The Board.js constructor creates every cell with a false value.
The board also has a "Reset" button to reset all Cell's back to false.
The values of the Cells changes correctly when I click on them. When "Reset" button is clicked, I want every Cell's isAlive value to be false. However, when "Reset" button is pressed visually there is no change on the board (The Cell's colors doesn't change). The console.log(this.state.cells); line on handleClick() method on Board.js prints props: Object { isAlive: false } for all Cells. So why the Cell's are not updating visually? What am I doing wrong here?
Board.js
export default class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
cells: []
};
this.rowNum = 10;
this.colNum = 10;
for (let i = 0; i < (this.rowNum * this.colNum); i++) {
this.state.cells.push(<Cell isAlive={false}/>);
}
this.handleClick = this.handleClick.bind(this);
this.getCols = this.getCols.bind(this);
}
getCols() {
let cols = [];
for (let i = this.rowNum; i <= (this.rowNum * this.colNum); i += this.rowNum) {
cols.push(this.state.cells.slice(i - this.rowNum, i).map(function (item, i) {
return <div key={i}>{item}</div>
}));
}
return cols;
}
handleClick() {
const newBoard = this.state.cells;
newBoard.forEach((item, index, arr) => {
arr[index] = <Cell isAlive={false}/>
});
this.setState({
cells: newBoard
}, function () {
console.log(this.state.cells);
}
);
}
render() {
return (<div className="background">
<table className="gameBoard">
<tbody>
<tr>
{this.getCols().map(function (item, index) {
return <td className="col" key={index}>{item}</td>
})}
</tr>
</tbody>
</table>
<button onClick={this.handleClick}> Reset</button>
</div>
);
}
}
The significant parts of the Cell.js:
export default class Cell extends React.Component {
constructor(props) {
super(props);
this.colors = {
dead: '#041b40',
alive: '#b2e8f7',
hover_dead: '#495d76',
hover_alive: '#e6fffd'
};
this.state = {
isAlive: this.props.isAlive,
isMouseDown: false,
isHovering: false
};
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({
isAlive: !this.state.isAlive,
isMouseDown: this.state.isMouseDown,
isHovering: this.state.isHovering
})
}
...
determineColor() {
if (this.state.isAlive && this.state.isHovering) {
return this.colors.hover_alive;
} else if (!this.state.isAlive && this.state.isHovering) {
return this.colors.hover_dead;
} else if (this.state.isAlive && !this.state.isHovering) {
return this.colors.alive;
}else {
return this.colors.dead;
}
}
render() {
return (
<div>
<button className='square'
onClick={this.handleClick}
type='button'
style={{
backgroundColor: this.determineColor()
}}>
</button>
</div>
)
};
}
You need to update the inner state on props change:
export default class Cell extends React.Component {
constructor(props) {
super(props);
this.colors = {
dead: '#041b40',
alive: '#b2e8f7',
hover_dead: '#495d76',
hover_alive: '#e6fffd'
};
this.state = {
isAlive: this.props.isAlive,
isMouseDown: false,
isHovering: false
};
this.handleClick = this.handleClick.bind(this)
}
componentDidUpdate(prevProps, prevState) { //<--HERE
this.setState({isAlive: this.props.isAlive})
}
handleClick() {
this.setState({
isAlive: !this.state.isAlive,
isMouseDown: this.state.isMouseDown,
isHovering: this.state.isHovering
})
}
...
determineColor() {
if (this.state.isAlive && this.state.isHovering) {
return this.colors.hover_alive;
} else if (!this.state.isAlive && this.state.isHovering) {
return this.colors.hover_dead;
} else if (this.state.isAlive && !this.state.isHovering) {
return this.colors.alive;
}else {
return this.colors.dead;
}
}
render() {
return (
<div>
<button className='square'
onClick={this.handleClick}
type='button'
style={{
backgroundColor: this.determineColor()
}}>
</button>
</div>
)
};
You must use the componentDidUpdate
componentDidUpdate(prevProps) {
if (this.props.isAlive!== prevProps.isAlive) {
this.setState({isAlive: this.props.isAlive})
}
}

HandleClick not changing the state

I have the below where it should display images of beers retrieved from an API. Each image has a handleClick event which will direct them to a details page about this beer. My code below doesn't render the beers at all and goes straight to the details page of a random beer. Can anyone help me figure out why?
Thanks
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
showMethod: false,
beerDetails: []
};
this.getBeerInfo = this.getBeerInfo.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick(details) {
this.setState({
showMethod: !this.state.showMethod,
beerDetails: details
});
}
render() {
if(this.state.showMethod) {
return (
<Beer details = {this.state.beerDetails}/>
);
}
else {
return (
<div>{this.state.beers.map(each=> {
return <img className = "img-beer" onClick = {this.handleClick(each)} src={each.image_url}/>
})}</div>
);
}
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo() {
...gets info
}
}
When you use onClick like that you run the function at the render.
So you have to use arrow function:
Not Working:
<img className = "img-beer" onClick = {this.handleClick(each)} src={each.image_url}/>
Working:
<img className = "img-beer" onClick = {() => this.handleClick(each)} src={each.image_url}/>
The main issue is not calling the handle properly.
Also, I noticed that you are binding the functions in the constructor. It might be simpler to use ES6 function creation, so the scope of the class is bound to your handle method.
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
showMethod: false,
beerDetails: []
};
}
handleClick = (details) => {
this.setState({
showMethod: !this.state.showMethod,
beerDetails: details
});
}
render() {
if(this.state.showMethod) {
return (
<Beer details = {this.state.beerDetails}/>
);
}
else {
return (
<div>{this.state.beers.map(each=> {
return <img className = "img-beer" onClick = {() => this.handleClick(each)} src={each.image_url}/>
})}</div>
);
}
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo = () => {
...gets info
}
}

React - How to set a state on a children when clicking on the parent

I have a search bar on a children component who's displaying a pannel when i'm writing inside,
and I'm trying to close it on my parent when I'm clicking outside of this pannel.
So I need a solution to set a state to false at this moment.
Any idea ?
Here's an example of my code:
[parent.js]
constructor(props) {
super(props)
this.state = {
showResults: false
}
}
render() {
return (
<div onClick={() => this.setState({showResults: false})}>
<div>
<Children showResults={this.state.showResults}/>
</div>
</div>)
}
[children.js]
constructor(props) {
super(props)
this.state = {
showResults: this.props.showResults
}
}
getResults(event) {
this.setState({input: event.target.value, showResults: true})
}
render() {
return (<div>
<Input onChange={this.getResults.bind(this)}/>
this.state.showResults ?
<div>PANNEL</div>
: ''
</div>)
}
When the panel is open, create a transparent div that covers the screen behind your panel with an onClick property to change the state.
I finally did it using Redux, I have created a ShowPannel reducer who just have a boolean visible state inside. Here's my code:
[My view] :
render() {
return (<div onClick={() => this.props.showPannel(false)}>
{
this.props.visible
? <div className="listResults"></div>
: ''
}
</div>)
}
function mapStateToProps(state) {
return {visible: state.showPannelReducer.visible};
}
function mapDispatchToProps(dispatch) {
return {
showPannel: (visible) => dispatch(showPannel(visible))
}
}
Home = connect(mapStateToProps, mapDispatchToProps)(Home);
export default Home;
[My reducer] :
import {SHOW_PANNEL} from '../actions/pannel/types';
const initialState = {
visible: false
};
function showPannelReducer(state = initialState, action) {
switch (action.type) {
case SHOW_PANNEL:
return {
...state,
visible: action.visible
}
default:
return state
}
};
export default showPannelReducer;

How to render multiple component inside map function in REACT?

I am using switch case statement in which i am using two map function. one map function is used for word and other is used for punctuation mark. basically i want to split a word with punctuation mark.
here i face this problem in which everything is going fine but my component in not render. in if else
what should i do call this component whose name is A and B.
</div>
<script type="text/babel">
class A extends React.Component {
constructor(props){
super(props);
this.state={
}
}
render(){
return (
<div>
{this.props.item}hello
</div>
)
}
}
class B extends React.Component {
constructor(props){
super(props);
}
render(){
return(
<div>
{this.props.items}
</div>
)
}
}
class C extends React.Component {
constructor(props){
super(props);
self =this;
this.state = {
cstate:true,
renderdComp:''
}
this.switch =this.switch.bind(this)
this.callComp =this.callComp.bind(this)
}
callComp(item,punc,k){
console.log("item =>",item,"punc =>",punc,"k =>",k);
if(punc == '') {
this.setState({
renderdComp : "A"
})
return <A item={item}/>
} else {
this.setState({
renderdComp : "B"
})
return <B items={item}/>
}}
switch(stateValue) {
if(stateValue) {
let freshItem;
let puncItem;
['a;','as','df','fg'].map( function(item, i) {
[';',':','<'].map( function (punc, j) {
const isFoundPunc = item.indexOf(punc)
if(isFoundPunc > -1) {
console.log(isFoundPunc)
freshItem = item.substr(0,isFoundPunc);
console.log(freshItem)
puncItem = item.substr(isFoundPunc,item.length);
console.log(puncItem)
} else {
freshItem = item
console.log(freshItem+"sec")
puncItem = ''
console.log(puncItem+"sec")
}
})
self.callComp(freshItem, puncItem, i)
})
}
}
render(){
return(
{this.switch(this.state.cstate)}
)
}
}
ReactDOM.render(,document.getElementById("container"));
You can setState here in this function
constructor() {
this.callComp = this.callComp.bind(this)
}
callComp(item,punc,k){
console.log("item =>",item,"punc =>",punc,"k =>",k);
if(punc == '') {
this.setState({
renderdComp : "A"
})
return <A item={item}/>
} else {
this.setState({
renderdComp : "B"
})
return <B items={item}/>
}
This should rerender your component C

React calling methods in different conditions

I'm beginner on react and i've written the code below:
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
save() {
this.props.onChange(this.refs.newVal.value, this.props.id);
this.setState({editing: !this.state.editing});
// console.log('save is over');
}
renderForm() {
return (
<div className="note">
<textarea ref="newVal"></textarea>
<button onClick={this.save}>SAVE</button>
</div>
);
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={this.edit}>EDIT</button>
<button onClick={this.remove}>X</button>
</span>
</div>
);
}
render() {
console.log(this.state.editing);
return (this.state.editing) ? this.renderForm()
: this.renderDisplay()
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
In render(), onClick event has a function, that is, if used in this way: {this.add} the following error is created:
Uncaught Error: Objects are not valid as a React child (found: object with keys {dispatchConfig, _targetInst, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, ...})
Why? while in the eachNote() method this command is used:
onChange={this.update}
And there was no error.
Someone can tell me the reason? thanks.
The problem is that in the add function you are taking an argument text and setting it in the state so when you call onClick={() => this.add()}, you are not passing any argument to add function and hence in its definition text is undefned and hence state note is set as undefined.
However if you directly call it like onClick={this.add} , the add function receives the event object as a parameter and hence it sets state note to be an event object which you are using to render
onClick={this.add} will pass the click event to this.add.
So what you need to do is either:
onClick={e => this.add('some text')} or similar.
If you want to onClick={this.add} you have to ensure that your add method is: add(event) { ... } instead.
The <Note /> component does not contain a render() method to return anything. Add a render() method and return something.
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
render() {
return (
<div>Render something</div>
)
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="root"></div>

Categories

Resources