I have a chess game I am building with React-Redux...
The issue I am having is how to highlight the squares available for a piece to move to with my current architecture.
I've tried passing data from the children to the parent via props, but this doesn't seem to work. I've also tried using refs and ran into a lot more goofy issues. I think I am missing something obvious here...
<Board/>
is made up of an array of 64:
<Square key={} />
components. Each has a key prop that is a chess square ID, like "a8" or "f6".
I have a method of my App component that returns an array of available squares to move to when one clicks a piece.
calculateAvailableSquares(pos, piece, board){
//returns an array like ["f5", "f4","f3"]
//TODO get this function to highlight the <Square/>s
/// that have a key within the returned array
}
How can I fire a method in my App component and pass an updated className or style prop to all and only the Square components who's keys are in the array?
I don't understand how to select these children in React, and I don't understand how, once I would have them selected, I would alter them dynamically like this.
You mentioned you are using Redux, so you could set your global state of which squares are highlighted by calling one of your Redux reducers.
Alternatively, a quicker and easier way to do it would be to pass a function from the parent component to all the child components.
class ParentComponent {
state = {
highlightedSquares: []
}
setSquareState = square => {
const highlightedSquares = this.state.highlightedSquares.push(square)
this.setState({ highlightedSquares })
}
render() {
<div>
<Square setSquareState={this.setSquareState} />
</div>
}
}
const Square = props => (
<div className={props.isHighligted ? "highlighted" : ""}>
<button onClick={props.setSquareState}>Highlight</button>
</div>
)
Related
I am building a react app and have two functional components which do not have a parent/ child relationship.
Component one renders a canvas element and has functions which alter that element.
Component two is for the UI and has buttons which I want to trigger component one's functions.
The app is quite complex and I want to keep all the canvas functions in one place not in the global app scope.
My question is how do I reference component one functions in component two.
Component one:
export default function CanvasElement() {
let drawImage = () => {
/* Alter canvas */
}
return (
<div>
<canvas id="image-region-canvas"></canvas>
</div>
)
}
Component two:
export default function UIElement() {
return (
<div>
<button onClick={canvasElement.drawImage}></button>
</div>
)
}
App.js:
class App extends Component {
render() {
return (
<div className="card-designer">
<CanvasElement/>
<UIElement/>
</div>
);
}
}
You actually need to shift the drawImage() function up to the App component and pass this as props to both or essentially UIElement.
You should treat <App> like a container, or wrap both child components in a container parent inside <App>. Then, place the functions in the parent and pass what they return as props to CanvasElement. That way you can call the functions using button clicks and pass the result to the other child.
Why don't you use a container component?
export default function Container() {
let drawImage = () => {
/* Alter canvas */
}
return (
<div>
<CanvasElement/>
<UIElement drawImage={drawImage}/>
</div>
)
}
To better re-usability you have to use both presentation components and container components.
All the business logic or data manipulation needs to handle by container components. Presentation components are only responsible for displaying the data that get from props.
So with the above implementation, you can keep all your business logic data in one place (In this case, inside the container component) Then you can pass down any necessary things as props to child components.
I am still fairly new to Reactjs and I am struggling with one thing.
I have built a small React app that consists of:
App.js at the very top –the only class component that controls all the states
3 stateless components ( no1 , no2, no3 rendered in App.js (all with smaller components inside them, but that’s irrelevant)
App.js state includes 3 arrays all of which are used by those 3 stateless components rendered.
And then, in App.js I have around 400 lines of methods where I am modifying state by calling setState. That’s quite long....
My question is: is there anyway to split this file? To move methods to their respective components: no 1, 2 and 3.
It seems impossible to me as having all the states in one class requires calling setState (having setState methods) in the same class only.
This might be a stupid question but:
Is it possible to modify state outside parent that holds this state (App.js), for instance, modify it in a stateless component no 1, and still keep parent updated about the change so that it can inform stateless components no 2 and no 3 about the change.
What’s the best practice in my case?
thanks
What i do for code/logic splitting is:
Keep the App.js as simple as possible
If there is a lot of logic in a file/Component, i split the file to index.js and Component.js and keep the logic in index.js and the JSX/HTML in the Component.js
In your case you can:
Remove all logic from App.js
Create 3 folders no1, no2 and no3
Inside each folder create two files - index.js and ComponentNo1.js etc
In the index.js create a Component with state and in ComponentNo1.js create a stateless component that returns the JSX/HTML.
This is what I would recommend. If you find it hard to do it, post your App.js logic so that I could help you do it.
Sure something like this is completely valid and is a common React practice.
I've written a sandbox to give you an idea of how this works:
https://codesandbox.io/s/zxl2owp2np
But to explain in detail, let's assume you have the following components:
class App extends React.Component {
state = {
value: 5
};
increaseValue = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<div className="App">
<Example1 value={this.state.value} increaseValue={this.increaseValue} />
<Example2 value={this.state.value} />
<Example3 value={this.state.value} />
</div>
);
}
}
const Example1 = (props) => {
const value = props.value;
return (
<div>
I am Example1. The value is {value}
<button onClick={() => props.increaseValue()}>Click</button>
</div>
);
}
const Example2 = (props) => {
const value = props.value;
return (
<div>
I am Example2. The value is {value}
</div>
);
}
const Example3 = (props) => {
const value = props.value;
return (
<div>
I am Example3. The value is {value}
</div>
);
}
Our main App component has a state value of 5 that we pass into our 3 child components. The child components are all using that value to display data. In the App component, we defined a state-updating function called increaseValue, which we pass in to Example1 as property. Now Example1 can use increaseValue by calling props.increaseValue()
props.increaseValue() is bound to our App component's execution context. So it will update the value in App's component state. Now with the updated value, it gets shared across all our Example components for use.
Regarding your question about moving methods to the components that are using them. This is not a common practice. Typically you define the function inside the component where the state is going to be updated. Meaning, if you have a function that is meant to update the App component, it should be defined in the app component.
I've been trying to wrap my head around this problem for a while. I've hacked together a solution that works, until I get any nested divs, then things fall apart. Basically what I'm trying to do is create composition components that live within a higher order component and all share the same current state. I then need to export that so that any file can use those components. So here's what the JSX might look like:
<Panel countersStartAt=5>
<Counter incrementsBy=1 />
<div>
<Counter incrementsBy=2 />
</div>
<TotalCounter className="someclass" />
</Panel>
So the way I want something like this to work is that I have this wrapper Panel component that sets some initial state, say this.state.start = 5. Within Panel, a Counter component would have an onClick handler that increments state.start by incrementsBy. And TotalCounter would be a component that displayed state.start. Of course this is a contrived example, so it would be helpful not to bring up how I could make this particular component better. I'm looking to apply this to a more realistic situation.
The second thing would be how to export those components in a way that I can create the exact code above in a separate file within a stateless component. Hopefully that makes sense.
This is a snippet of what I'm doing to achieve this.
renderChildren = (children) => {
return React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.createElement(
(child.type.name ? this[child.type.name] : child.type),
child.props
);
}
return child;
});
};
render = () => {
return (
{this.renderChildren(this.props.children)}
)
};
Then outside of the Panel class I'm exporting like so:
export const Counter = () => null;
Just so it exposes Counter. The default render of null doesn't happen because I replace Counter with the this.Counter() method within Panel.
Questions asked in Comments and Other things to consider
I am not using Flux or Redux
Assume that the Panel code snippet is used in several render methods across several projects that do not implement Flux patterns or Redux
Assume that those code snippets can't be re-written
How can can Panel, Counter, and TotalCounter be exported? Is it possible to do this for Counter and TotalCounter as they are methods within the Panel class? My research led to no, and creating "dummy" components to be exported so that the current file can use them without errors.
To put in an answer here for what we talked about in the chat room
the best way to handle what you want to do without a data management framework like Redux or Flux is to pass your data as props through, like so.
class Panel extends Component {
constructor(){
super()
this.state = {count: 5}
}
incrementCount = (incrementer) => {
this.setState({count: this.state.count + incrementer});
}
render (){
return (
<div>
<Counter incrementCount={this.incrementCount} count={this.state.count} incrementsBy=2 />
</div>
);
}
}
then in your counter..
<someElement onClick={ (e) => {this.props.incrementCount(this.props.incrementsBy)} }>{this.props.count}</someElement>
I have a main component App containing some children according to the routes (I use react-router) etc :
class App extends Component {
otherClick = () => { /* run every children's `handleButton2` function */ }
<div className="App">
<Button handleMenuClick={this.toggleSideBar}>Button 1</Button>
<Button handleOtherClick={this.otherClick}>Button 2</Button>
<SideBar ref="sideBar" title="Toto"/>
{this.props.children}
</div>
}
So, according to the route, App will contain some other containers such as:
class ContainerABC extends React.Component {
constructor(props) {
super(props);
}
handleButton2 = () => {
let sc = this.refs.subCont;
sc.setState({visible : !sc.visible});
// Change the color of Button 2 ???
};
render() {
return (
<div>
<SubContainer ref="subCont"/>
</div>
);
}
};
The role of Button 2 depends on the current Container. In the example above, when I have a ContainerABC as child, I want that Button 2 toggles the SubContainer of ContainerABC.
How can I tell to Button 2 to do the appropriate action according to the child of the component ?
And/or how can I modify Button 2 (or any trigger) from SubCont when Button 2 triggers an action on SubCont ?
Maybe using Redux ? I don't see how it could be helpful
Redux might help only because it can trigger an action that, in return, modifies the global state tree (e.g. redux store through a reducer). If that's the only purpose you need fulfilling, then I'd recommend against adding complexity (as much as I fancy Redux).
I assume you want a random child from {this.props.children} fire a random action once Button 2 is clicked?
Let's observe this commonly enforced React pattern:
Properties flow downwards. Actions (read: callbacks) go upwards.
That said, you may want to iterate through your {this.props.children} and check for the existence of a special callback prop that adheres to your API requirements.
React.Children.forEach(this.props.children, (child) => {
if (typeof child.props.toggleButton2State !== "function") {
throw('Woah, cowboy, you need that toggleButton2State function);
}
}
Then your button could cycle through children in the same manner and execute that function, if exists.
handleButton2Click() {
React.Children.forEach(this.props.children, (child) => {
if (typeof child.props.toggleButton2State === "function") {
child.props.toggleButton2State.call(child, !oldState, this);
}
}
}
So you just called child's callback function in scope of the child with boolean state being toggled and you also passed the reference to the parent component (this).
I would strongly suggest you never manipulate the parent container from a child. You never know how your hierarchy may change.
Obviously, this is a very rough example but it should get you going. Let me know how it goes.
If the behavior of the button depends on what container is being rendered, then it sounds to me like the container should render the buttons. You could wire up some props (could even use cloneElement to put them on the children) so you can pass callbacks down that would change the behavior of the button, but that sounds like a nightmare to maintain.
You could put those buttons in a separate component (with a prop to determine what they do) and render it in the containers. That sounds much simpler to me.
I have a listview component which consists of a number of child listitem components.
Each child listitem have a showSubMenu boolean state, which display a few extra buttons next to the list item.
This state should update in response to a user event, say, a click on the component DOM node.
childcomponent:
_handleClick() {
... mutate state
this.props.onClick() // call the onClick handler provided by the parent to update the state in parent
}
However, it feels somewhat wrong to update state like, as it mutates state in different places.
The other way i figured i could accomplish it was to call the this.props.onClick directly, and move the child state into the parent as a prop instead, and then do change the state there, and trickle it down as props.
Which, if any, of these approaches is idiomatic or preferable?
First of all, I think that the question's title doesn't describe very well what's your doubt. Is more an issue about where the state should go.
The theory of React says that you should put your state in the higher component that you can find for being the single source of truth for a set of components.
For each piece of state in your application:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy).
Either the common
owner or another component higher up in the hierarchy should own the
state.
If you can't find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
However, a Software Engineer at Facebook said:
We started with large top level components which pull all the data
needed for their children, and pass it down through props. This leads
to a lot of cruft and irrelevant code in the intermediate components.
What we settled on, for the most part, is components declaring and
fetching the data they need themselves...
Sure, is talking about data fetched from stores but what im traying to say is that in some cases the theory is not the best option.
In this case i would say that the showSubMenu state only have sense for the list item to show a couple of buttons so its a good option put that state in the child component. I say is a good option because is a simple solution for a simple problem, the other option that you propose means having something like this:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item} </div>
);
}, this)}
</div>
);
}
});
If, in a future, the list view has to get acknowledge of that state to show something for example, the state should be in the parent component.
However, i think it's a thin line and you can do wathever makes sense in your specific case, I have a very similar case in my app and it's a simple case so i put the state in the child. Tomorrow maybe i must change it and put the state in his parent.
With many components depending on same state and its mutation you will encounter two issues.
They are placed in component tree so far away that your state will have to be stored in a parent component very high up in the render tree.
Placing the state very high far away from children components you will have to pass them down through many components that should not be aware of this state.
THERE ARE TWO SOLUTIONS FOR THIS ISSUE!
Use React.createContext and user context provider to pass the data to child elements.
Use redux, and react-redux libraries to save your state in store and connect it to different components in your app. For your information react-redux library uses React.createContext methods under the hood.
EXAMPLES:
Create Context
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
REDUX AND REACT-REDUX
import { connect } from 'react-redux'
const App = props => {
return <div>{props.user}</div>
}
const mapStateToProps = state => {
return state
}
export default connect(mapStateToProps)(App)
For more information about redux and react-redux check out this link:
https://redux.js.org/recipes/writing-tests#connected-components