I'm trying to replicate this example. It won't work somehow. From components higher in the hierarchy to lower ones, I have three files:
index.js:
import React, { Component } from "react"
import { SelectableGroup } from "react-selectable-fast"
import List from "../components/List"
const items = [
{
player: "Dirk Nowitzki",
year: 1999,
},
{
player: "Magic Johnson",
year: 1980,
},
{
player: "Michael Jordan",
year: 1990,
},
]
const IndexPage = () => (
<SelectableGroup
className="main"
clickClassName="tick"
// enableDeselect
// tolerance={this.state.tolerance}
// globalMouse={this.state.isGlobal}
// allowClickWithoutSelected={false}
// duringSelection={this.handleSelecting}
duringSelection={() => {
console.log("DURING")
}}
// onSelectionClear={this.handleSelectionClear}
onSelectionFinish={() => {
console.log("FINISH")
}}
>
<List items={items} />
</SelectableGroup>
)
export default IndexPage
List.js
import React, { Component } from "react"
import SelectableComponent from "./Film"
export default class List extends Component {
render() {
console.log(this.props.items)
return (
<>
{this.props.items.map((item, i) => (
<SelectableComponent key={i} player={item.player} year={item.year} />
))}
</>
)
}
}
Player.js
import React from "react"
import { createSelectable } from "react-selectable-fast"
const Player = ({ selectableRef, selected, selecting }) => (
<div
ref={selectableRef}
style={{
border: "1px solid blue",
width: "300px",
height: "300px",
float: "left",
}}
className="tick"
>
{console.log(selected)}
{console.log(selecting)}
</div>
)
export default createSelectable(Player)
It doesn't throw any errors, but the console.log in Player.js are always undefined. OnSelectionFinish and DuringSelection work fine, at least the console.log are coming through.
The variable names need to be changed they are isSelected and isSelecting in your Player.js
A good rule of thumb is to print your whole props object when debugging.
Further code can be found on the library's github page.
import React from 'react'
import { TSelectableItemProps, createSelectable } from 'react-selectable-fast'
class SomeComponent extends Component<TSelectableItemProps> {
render() {
const { selectableRef, isSelected, isSelecting } = this.props
return <div ref={selectableRef}>...</div>
}
}
export default createSelectable(SomeComponent)
Related
so i'm trying to implement a simple theme switch via react context, and i need to change the value of context(in ThemeProvider.jsx) provider according to a onChange event in another component(ThemeSwitcher.jsx).
ThemeProvider.jsx :
import React, {createContext} from "react";
import {THEME_TYPE} from "../constants";
export const ThemeContext = createContext(THEME_TYPE.LIGHT);
const ThemeProvider = ({ children }) => {
return <>
<ThemeContext.Provider value={//THEME_TYPE.LIGHT or THEME_TYPE.DARK)}>
{children}
</ThemeContext.Provider>
</>
};
export default ThemeProvider;
ThemeSwitcher.jsx :
import React, {useContext} from "react";
import { THEME_TYPE } from "../constants";
import {ThemeContext} from "../providers/ThemeProvider";
const ThemeSwitcher = () => {
const themeMode = useContext(ThemeContext);
const handleThemeChange = (e) => {
//value of context should change according to argument 'e'
};
return (
<div className="switch-container">
<label className="switch">
<input
data-testid="theme-changer"
type="checkbox"
checked={themeMode === THEME_TYPE.DARK}
onChange={handleThemeChange}
/>
<span className="slider round"></span>
</label>
<span className="text-color bold">Dark mode</span>
</div>
);
};
export default ThemeSwitcher;
App.jsx:
import React, {useContext} from "react";
import { Helmet } from "react-helmet";
import NameBox from "./components/NameBox";
import ThemeSwitcher from "./components/ThemeSwitcher";
import { THEME_TYPE } from "./constants";
import Styles from "./data/Styles";
import ThemeProvider from "./providers/ThemeProvider";
import {ThemeContext} from "./providers/ThemeProvider";
const StyleTag = () => {
const themeMode = useContext(ThemeContext);
return (
<Helmet>
<style>{Styles(themeMode)}</style>
</Helmet>
);
};
function App() {
return (
<ThemeProvider>
<StyleTag />
<NameBox />
<ThemeSwitcher />
</ThemeProvider>
);
}
export default App;
and styles.js if necessary:
import { THEME_TYPE } from "../constants";
const Theme = {
[THEME_TYPE.LIGHT]: {
background: "#fafafa",
text: "#rgba(0, 0, 0, 0.87)",
divider: "rgba(0, 0, 0, 0.12)",
},
[THEME_TYPE.DARK]: {
background: "#303030",
text: "#fff",
divider: "rgba(255, 255, 255, 0.12)",
},
};
const Styles = (theme) => `
body {background-color: ${Theme[theme].background};}
.text-color {color: ${Theme[theme].text};}
.box {border: 1px solid ${Theme[theme].divider}}
`;
export default Styles;
as you see the value of context should be changed according to input onChange event. i couldn't come up with proper solution for relating these two, so your help is appreciated.
I suggest adding useState() hook inside ThemeProvider component.
Here is the codesandbox: https://codesandbox.io/s/magical-franklin-cril0?file=/src/ThemeProvider.jsx
That is how the code looks like:
import React, { createContext, useState } from "react";
import { THEME_TYPE } from "./constants";
export const ThemeContext = createContext(THEME_TYPE.LIGHT);
const ThemeProvider = ({ children }) => {
const [themeType, setThemeType] = useState(THEME_TYPE.LIGHT);
const changeTheme = (value) => {
if (value) {
setThemeType(THEME_TYPE.DARK);
} else {
setThemeType(THEME_TYPE.LIGHT);
}
};
return (
<>
<ThemeContext.Provider value={{ themeType, changeTheme }}>
{children}
</ThemeContext.Provider>
</>
);
};
export default ThemeProvider;
And then you would use the context where needed like this:
const themeMode = useContext(ThemeContext)
themeMode.themeType // THEME_TYPE.LIGHT or THEME_TYPE.DARK
themeMode.changeTheme(value) // if value is true, it would change to dark mode, if false to light mode
I have 2 root components in react360.I have the main panel with information and my products and i have my 3d model.I want to communicate one another.Μore specifically I just want to click on the product then the color from my 3d model changes.At this time, my 3d model has value from the store I have originally defined, but while the price is changing, it is not renewed in the 3d model.for example when the application starts the original color of my model is blue but when I click on the first item it does not change to red. Wrere is the problem?????before
after
client.js
import { ReactInstance } from "react-360-web";
import { Location } from "react-360-web";
function init(bundle, parent, options = {}) {
const r360 = new ReactInstance(bundle, parent, {
// Add custom options here
fullScreen: true,
...options
});
// Render your app content to the default cylinder surface
r360.renderToSurface(
r360.createRoot("Center", {
/* initial props */
}),
r360.getDefaultSurface()
);
r360.renderToLocation(
r360.createRoot("React3DView"),
r360.getDefaultLocation()
);
// Load the initial environment
r360.compositor.setBackground(r360.getAssetURL("360_world.jpg"));
}
window.React360 = { init };
index.js
import { AppRegistry } from "react-360";
import Center from "./components/center";
import React3DView from "./components/obj";
AppRegistry.registerComponent("Center", () => Center);
AppRegistry.registerComponent("React3DView", () => React3DView);
reducer
initialState = {
data: [
{ id: 1, value: "MILEPTY.png" },
{ id: 2, value: "cleveland.png" },
{ id: 3, value: "phila.png" },
{ id: 4, value: "raptors.png" },
{ id: 5, value: "rockets.png" }
],
currentinfo: "Hello.Press click on T-shirt to show information",
currentcolor: "blue"
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "INFO":
if (action.key === 1) {
return {
...state,
currentinfo: "Milwaukee bucks",
currentcolor: "red"
};
}
if (action.key === 2) {
return {
...state,
currentinfo: "Cleveland Cavaliers",
currentcolor: "green"
};
}
if (action.key === 3) {
return { ...state, currentinfo: "Philadelphia 76xers" };
}
if (action.key === 4) {
return { ...state, currentinfo: "Toronto Raptors" };
}
if (action.key === 5) {
return { ...state, currentinfo: "Huston Rockets" };
}
default:
return state;
}
};
export default reducer;
centerPanel
import React from "react";
import { AppRegistry, StyleSheet, Text, View, Image, asset } from "react-360";
import Products from "./products";
import { connect } from "react-redux";
class CenterPanel extends React.Component {
render() {
return (
<View style={styles.panel}>
<View style={{ flex: 1, flexDirection: "row" }}>
<View
style={{
width: 250,
height: 600
}}
>
<Text style={{ marginTop: "100" }}>{this.props.currentinfo}</Text>
</View>
<View
style={{
width: 1000,
height: 600,
backgroundColor: "green"
}}
>
<View style={{ flex: 1, flexDirection: "row" }}>
{this.props.data.map(element => (
<Products
key={element.id}
value={element.value}
id={element.id}
/>
))}
</View>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
panel: {
// Fill the entire surface
width: 1000,
height: 600,
backgroundColor: "rgba(255, 255, 255, 0.4)"
}
});
const mapStateToProps = state => {
return {
data: state.data,
currentinfo: state.currentinfo
};
};
export default connect(mapStateToProps)(CenterPanel);
products
import React from "react";
import { AppRegistry, StyleSheet, Text, View, Image, asset } from "react-360";
import { connect } from "react-redux";
class Products extends React.Component {
state = {
img: this.props.value
};
render() {
return (
<View
style={styles.panelimages}
onInput={() => this.props.onText(this.props.id)}
>
<Image style={styles.images} source={asset(this.state.img)} />
</View>
);
}
}
const styles = StyleSheet.create({
panelimages: {
width: 150,
height: 150,
marginTop: 200,
backgroundColor: "white"
},
images: {
width: 150,
height: 150
}
});
const mapDispatchToProps = dispatch => {
return {
onText: id => dispatch({ type: "INFO", key: id })
};
};
export default connect(
null,
mapDispatchToProps
)(Products);
center
import React from "react";
import { AppRegistry, StyleSheet, Text, View, Image, asset } from "react-360";
import { createStore } from "redux";
import { Provider } from "react-redux";
import reducer from "../store/reducer";
import CenterPanel from "./centerPanel";
// const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
const store = createStore(reducer);
export default class Center extends React.Component {
render() {
return (
<Provider store={store}>
<CenterPanel />
</Provider>
);
}
}
objpanel
import React from "react";
import {
asset,
AppRegistry,
StyleSheet,
Text,
View,
VrButton,
Image
} from "react-360";
import Entity from "Entity";
import { connect } from "react-redux";
class Object3d extends React.Component {
render() {
return (
<View>
<Entity
source={{ obj: asset("t-shirt.obj") }}
style={{
transform: [{ translate: [-3.5, -3.5, -2.8] }],
color: this.props.currentcolor -------->here is problem
}}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
currentcolor: state.currentcolor
};
};
export default connect(mapStateToProps)(Object3d);
obj
import React from "react";
import { AppRegistry, StyleSheet, Text, View, Image, asset } from "react-360";
import { createStore } from "redux";
import { Provider } from "react-redux";
import reducer from "../store/reducer";
import Object3d from "./objpanel";
// const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
const store = createStore(reducer);
export default class React3DView extends React.Component {
render() {
return (
<Provider store={store}>
<Object3d />
</Provider>
);
}
}
I've tried to do this with redux but in the end I had more problems implementing it than it was worth. In order to implement something like that you would need to follow the code that is described here:
React 360 multi panel example
Additionally, I've implemented what you are trying to do without redux. You can look at the code in this repository and also view the production link here. Its modeled after the react 360 code.
CryptoDashboardVR repo
CryptodashboardVR Multipanel synchronization
Finally, If you still need help, check out my course on React 360. I explain the concepts used. Udemy react 360.
Hope that helps. For now, I would abandon the redux approach until maybe 2.0 comes out.
So I am writing a simple program which takes selections off of a drawer and then show them as part of a recipe, but when the function from the parent is called the updated value sent to the parent is a number rather than the object. I'm not sure what i'm doing wrong and would really appreciate some help.
Parent:
import React, {Component} from 'react';
import {listOfOils} from "./Constants/OilList";
import OilDrawer from "./Components/OilDrawer";
import OilMixCard from "./Components/OilMixCard";
class App extends Component {
constructor(props) {
super(props)
this.state = {
recipe: [],
recipeName: ''
}
}
addOilToRecipe = (oil) => {
this.setState(() => {
return {recipe: this.state.recipe.push(oil)}
}
)
}
setRecipeName = (name) => {
this.setState(() => {
return {recipeName: name}
})
}
render() {
return (
<div className="App">
<OilDrawer
addOil={this.addOilToRecipe}
oils={listOfOils}
/>
{
console.log(JSON.stringify(this.state.recipe))
}
{/*The value for recipe when printed in the OilMixCard in the console is 1*/}
<OilMixCard
recipe={this.state.recipe}
updateName={this.setRecipeName}
/>
</div>
);
}
}
export default App;
Child:
import Divider from '#material-ui/core/Divider'
import Avatar from "#material-ui/core/Avatar";
import ListItem from "#material-ui/core/ListItem";
import List from "#material-ui/core/List";
import ListItemText from "#material-ui/core/ListItemText";
class OilDrawer extends React.Component {
constructor(props) {
super(props);
this.state = {
openDrawer: true
}
}
// handleDrawerClose = () => {
// this.setState({openDrawer: false})
// }
render() {
return (
<Drawer
className="oil_drawer"
variant='persistent'
anchor='right'
open={this.state.openDrawer}
>
{/*<IconButton onClick={this.handleDrawerClose}>*/}
{/*<ChevronRightIcon/>*/}
{/*</IconButton>*/}
<Divider/>
<List>
{this.props.oils.map(oil =>
<ListItem
button
onClick={() => this.props.addOil(oil)}
key={oil.name}
>
<Avatar src={oil.image}/>
<ListItemText primary={oil.name}/>
</ListItem>
)}
</List>
</Drawer>
)
}
}
export default OilDrawer
Another Child which displays the recipe:
import React from 'react'
import Paper from "#material-ui/core/Paper";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import Avatar from "#material-ui/core/Avatar";
import ListItemText from "#material-ui/core/ListItemText";
class OilMixCard extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<Paper>
<List>
{console.log(JSON.stringify(this.props.recipe))}
{
this.props.recipe.map(oil =>
<ListItem
button onClick={() => this.props.addOil(oil)}
key={oil.name}
>
<Avatar src={oil.image}/>
<ListItemText primary={oil.name}/>
</ListItem>
)
}
</List>
</Paper>
)
}
}
export default OilMixCard
The push method on arrays return the added value rather than the final array. In your addOilToRecipe method, its the string in reference oil that gets set to this.state.recipie, when you do the following:
this.setState({ recipe: this.state.recipe.push(oil) });
Now when you call map on the string, you get individual numeric indexes.
Instead do something like this:
this.setState({ recipe: [...this.state.recipe, oil] });
Wizard and WizardPage
I am trying to build a generic Wizard and WizardPages to be generic a reusable across my app. I think the Wizard component as the one in charge of managing the state, and the WizardPage that is going to work as a wrapper, and will render Back, Cancel and Next buttons. It's suppose that the Next button (maybe the Back too) should dispatch an redux action. This action, could be different depending on the component being wrapped. This is what i have (not complete version):
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import withWizardLayout from '../../hoc/WithWizardLayout';
class Wizard extends Component {
constructor(props) {
super(props);
this.state = {
page: 0,
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
};
previousPage = () => {
this.setState({ page: this.state.page - 1 });
};
render() {
const { wizardPages } = this.props;
const { page } = this.state;
const wizardItem = wizardPages[page];
const nextPage = this.nextPage;
const previousPage = this.previousPage;
const ComponentWrapped = withWizardLayout(wizardItem.ComponentWrapped, nextPage, previousPage);
return (
<ComponentWrapped />
);
}
}
export default Wizard;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { compose } from 'react-apollo';
import { connect } from 'react-redux';
import { Flex, Box } from 'reflexbox';
import { BlueRoundButton, GreyRoundButton } from '../components/Button';
export default function withWizardLayout(ComponentDependent, nextPageCallBack, previousPageCallback) {
class WizardPage extends Component {
previousPage = () => {
};
nextPage = () => {
this.props.test();
};
render() {
const { ComponentWrapped, isFirstPage, isLastPage } = ComponentDependent;
return (
<Flex column>
<ComponentWrapped
isLastPage={isLastPage}
isFirstPage={isFirstPage}
>
<Flex justify='flex-end'>
<Flex mr='auto' w={0.1}>
<GreyRoundButton style={{ fontWeight: '500', position: 'relative', top: '10%' }}>Back</GreyRoundButton>
</Flex>
<Flex w={0.08} mr={2} align='center' justify='center' style={{ textAlign: 'center' }}>
<span>Cancel</span>
</Flex>
<Flex w={0.15} justify='center'>
<BlueRoundButton onClick={this.nextPage} style={{ position: 'relative', top: '10%', fontWeight: '500' }}>Continue</BlueRoundButton>
</Flex>
</Flex>
</ComponentWrapped>
</Flex>
);
}
}
CustomersContainer
import { compose } from 'react-apollo';
import { connect } from 'react-redux';
import CustomerImport from '../components/Settings/CustomerImport';
import { withWizardLayout } from '../hoc/WithWizardLayout';
const mapStateToProps = null;
const mapDispatchToProps = dispatch => (
{
test: () => (dispatch(console.log("hi"))),
}
);
export default compose(connect(null, mapDispatchToProps))(CustomerImport);
Connected Component
import React, { Component } from 'react';
import styled from 'styled-components';
import {
SettingBlock,
NotifyBlock,
SuccessMessage,
ErrorMessage,
Instructions,
} from './styles';
import ExcelUploader from '../ExcelUploader';
const ProgressBar = styled.div`
width: 0;
height: 30px;
background-color: Green;
`;
const ExcelWrapper = styled.div`
margin-bottom: 4rem;
`;
export default class CustomerImport extends Component {
constructor(props) {
super(props);
this.state = {
progress: 0,
notify: {
success: {
message: '',
active: false,
},
error: {
message: '',
active: false,
},
},
};
}
render() {
const { success, error } = this.state.notify;
const ButtonsWrapper = this.props.children;
return (
<div>
<NotifyBlock>
<SuccessMessage className={success.active ? 'active' : null}>{success.message}</SuccessMessage>
<ErrorMessage className={error.active ? 'active' : null}>{error.message}</ErrorMessage>
</NotifyBlock>
<SettingBlock>
<h3>
Import your customers
</h3>
<ProgressBar style={{ width: `${this.state.progress}%` }} />
<Instructions>
Select an Excel file containing your customer information.
Need a template? Grab one here.
</Instructions>
<ExcelWrapper>
<ExcelUploader />
</ExcelWrapper>
{ButtonsWrapper}
</SettingBlock>
</div>
);
}
}
This is how i am supposed to render the Generic Wizard, we can have X wizardPages:
const wizardPages = [
{
ComponentWrapped: CustomersContainer,
isFirstPage: false,
isLastPage: false,
},
];
<Wizard wizardPages={wizardPages} />
The problem with this approach, is that i want on the onClick of the buttons in withWizardLayout:
1) Execute the callback on the Father (that's possible, i am passing as prop the handler)
2) Call the dispatch action received of the container. (i am not able not access the dispatched action, in this case, this.props.test)
How can i refactor this, to think a generic way to handle this? I think another ways to refactor this (having different withWizardLayout functions, but i am having render problems on the console).
Maybe i didn't architect this in the best way.
Help!
I am trying to implement Presentational and Container Components pattern when creating React components. So I created a presentational component with only UI elements and container component with handling data capabilities.
component.jsx
import React from "react";
const MyComponent = ({props}) => (
<div>
{props.games.map((game, index) => (
<div key={index}>
{game.index + 1} - {game.contestName}
</div>
))};
</div>
);
export default MyComponent;
container.jsx
import React, { Component } from "react";
import MyComponent from "./component";
class MyContainer extends Component {
constructor(props) {
super(props);
this.state = {
games: [
{
id: 1,
categoryName: "Business/Company",
contestName: "Name1"
},
{
id: 2,
categoryName: "Magazine/Newsletter",
contestName: "Name2"
},
{
id: 3,
categoryName: "Software Component",
contestName: "Name3"
},
{
id: 4,
categoryName: "Website",
contestName: "Name4"
}
]
};
}
render() {
return (
<div>
<MyComponent games={this.state.games} />
</div>
);
}
}
export default MyContainer;
However, I can not render data and I get
Uncaught TypeError:
Cannot read property 'games' of undefined.
Would really appreciate your help, as two days of internet digging has not yielded positive results.
const MyComponent = ({props}) => (
When you do this, you actually do
{ props: props-received-from-parent }
You are enclosing your props in another object, remove those braces and change that line to
const MyComponent = (props) => (
and you are good to go.
You should destructure your games instead of props:
import React from "react";
const MyComponent = ({games}) => (
<div>
{games.map((game, index) => (
<div key={index}>
{game.index + 1} - {game.contestName}
</div>
))};
</div>
);
export default MyComponent;
You can define your MyComponent class like this
class MyComponent extends Component{
render(){
this.xyz = this.props.games.map((item,index) => {
return(<div key={index}>{item.id}</div>)
})
return(
<div>
{this.xyz}
</div>
)
}
}
export default MyComponent;
This will also work!