Access to other components variables in react - javascript

I'm using react three fiber and i have two components
one to make a box and the other is to make an array of this box
here's how they look like, the platform component:
export function Plat() {
const [active, setActive] = useState(0)
const { scale } = useSpring({ scale: active ? 1 : 0.4 })
const [mat] = useState(() => new THREE.TextureLoader().load('/matcap.png'))
function Shape(props) {
return (
<animated.mesh {...props} scale={scale} onPointerOver={() => setActive(Number(!active))} >
<RoundedBox args={[60,20,60]} radius={4} smoothness={4} position={[0, -10, 0]} dispose={null} >
<meshMatcapMaterial matcap={mat} />
</RoundedBox>
</animated.mesh>
);
}
useEffect(() => {
(function() {
setActive((1))
})();
},[]);
return (
<Shape />
)
}
and the component that makes an array of this platform:
import {Plat} from './Plat'
export default function Boxes() {
function MyShape(props) {
return (
<mesh {...props} >
<Plat />
</mesh>
);
}
const [shapes, setShapes] = useState([<MyShape key={0} position={[100, 100, 100]} />, <MyShape key={1} position={[120, 120, 120]} />, <MyShape key={2} position={[130, 130, 130]} />]);
return (
<group >
{[...shapes]}
</group>
)
}
(I have more than 3 elements in the array)
I wanna know if there's a way to access the variables inside each of the array's platform components
how would I do something like this:
console.log(shapes[2].position) or change the position of this specific shape in the array
or this
shapes[1].setActive(1)
is it even possible?

Few pointer
Avoid nesting functional components
Plat
Shape // declare this outside and pass it the params from parent
If you need to change the position of the children, consider creating a array with the information you need to change
const [shapes, setShapes] = useState<>([{ key: 1, position: { x: 5, y: 10 } }])
const changeShapePostions = (key) => {
setShapes(items => {
return items.map(shape => {
if (shape.id === key) {
return { ...shape, position: { x: updatedX, y: updated: y} }
} else {
return shape
}
}
}
}
const setActive = (key) => {
setShapes(items => {
return items.map(shape => {
if (shape.id === key) {
return { ...shape, isActive: true }
} else {
return shape
}
}
}
}
return (
<group >
{
shapes.map(shape => {
return (<MyShape key={shape.key} position={shape.position} setActive={setActive} />)
}
}
</group>
)
you can check standard practices on working with arrays
Hope it helps in some ways

Related

Looping to clear all numbers colors in react

I have this working function to change all children button color on click.
Now im doing a "ClearGame" button, to change all button background color to its original state '#ADC0C4'. How can i do that with key or id from the children?
const newBet: React.FC = () => {
const clearGame = () => {
let spliceRangeJSON = gamesJson[whichLoteriaIsVar].range;
totalNumbers.splice(0, spliceRangeJSON);
for (let i = 0; i <= spliceRangeJSON; i++) {
//Looping to change the backgroundColor using Id or Key
}
};
const NumbersParent = (props: any) => {
const [numbersColor, setNumbersColor] = useState('#ADC0C4');
const changeButtonColor = () => {
if (numbersColor === '#ADC0C4') {
setNumbersColor(gamesJson[whichLoteriaIsVar].color);
totalNumbers.push(props.id);
} else {
setNumbersColor('#ADC0C4');
let searchTotalNumbers = totalNumbers.indexOf(props.id);
totalNumbers.splice(searchTotalNumbers, 1);
}
};
return (
<Numbers style={{ backgroundColor: numbersColor }} onClick={changeButtonColor}>
{props.children}
</Numbers>
);
};
return (
<NumbersContainer>
{numbersList.map((num) => (
<NumbersParent key={num} id={num}>
{formatNumber(num)}
</NumbersParent>
))}
</NumbersContainer>
<ClearGame onClick{clearGame}>Clear Game</ClearGame >
);
};
Since you want to modify the state of children from the parent component,
Create a State Object in the parent.
Pass it to the children as prop
You can change it on clear.
Or try something like recoil.
Move the useState and changeButtonColor method to the parent component and make some changes:
const [numbersColor, setNumbersColor] = useState({});
const changeButtonColor = (color) => {
if (!numbersColor[color] || numbersColor[color] === '#ADC0C4') {
setNumbersColor({...numbersColors,
[color]: gamesJson[whichLoteriaIsVar].color
});
totalNumbers.push(props.id);
} else {
setNumbersColor({...numbersColors,
[color]: '#ADC0C4'
});
let searchTotalNumbers = totalNumbers.indexOf(props.id);
totalNumbers.splice(searchTotalNumbers, 1);
}
};
Change the map to that:
<NumbersContainer>
{numbersList.map((num, index) => (
<NumbersParent key={num} id={num} index={index} changeButtonColor={(color) => {changeButtonColor(color)}}>
{formatNumber(num)}
</NumbersParent>
))}
</NumbersContainer>
And on the return of NumbersParent component changes to be like that:
<Numbers style={{ backgroundColor: numbersColor[`color${props.index}`] }} onClick={props.changeButtonColor(`color${props.index}`)}>
{props.children}
</Numbers>
Finally, change clearGame function like this:
const clearGame = () => {
let spliceRangeJSON = gamesJson[whichLoteriaIsVar].range;
for (let i = 0; i <= spliceRangeJSON; i++) {
changeButtonColor(`color${i}`)
}
totalNumbers.splice(0, spliceRangeJSON);
};

how to add multiple objects in reactjs?

I want to add new Objects when user click on checkbox. For example , When user click on group , it will store data {permission:{group:["1","2"]}}. If I click on topgroup , it will store new objects with previous one
{permission:{group:["1","2"]},{topGroup:["1","2"]}}.
1st : The problem is that I can not merge new object with previous one . I saw only one objects each time when I click on the group or topgroup.
onChange = value => checked => {
this.setState({ checked }, () => {
this.setState(prevState => {
Object.assign(prevState.permission, { [value]: this.state.checked });
});
});
};
<CheckboxGroup
options={options}
value={checked}
onChange={this.onChange(this.props.label)}
/>
Here is my codesanbox:https://codesandbox.io/s/stackoverflow-a-60764570-3982562-v1-0qh67
It is a lot of code because I've added set and get to set and get state. Now you can store the path to the state in permissionsKey and topGroupKey. You can put get and set in a separate lib.js.
In this example Row is pretty much stateless and App holds it's state, this way App can do something with the values once the user is finished checking/unchecking what it needs.
const Checkbox = antd.Checkbox;
const CheckboxGroup = Checkbox.Group;
class Row extends React.Component {
isAllChecked = () => {
const { options, checked } = this.props;
return checked.length === options.length;
};
isIndeterminate = () => {
const { options, checked } = this.props;
return (
checked.length > 0 && checked.length < options.length
);
};
render() {
const {
options,
checked,
onChange,
onToggleAll,
stateKey,
label,
} = this.props; //all data and behaviour is passed by App
return (
<div>
<div className="site-checkbox-all-wrapper">
<Checkbox
indeterminate={this.isIndeterminate()}
onChange={e =>
onToggleAll(e.target.checked, stateKey)
}
checked={this.isAllChecked()}
>
Check all {label}
</Checkbox>
<CheckboxGroup
options={options}
value={checked}
onChange={val => {
onChange(stateKey, val);
}}
/>
</div>
</div>
);
}
}
//helper from https://gist.github.com/amsterdamharu/659bb39912096e74ba1c8c676948d5d9
const REMOVE = () => REMOVE;
const get = (object, path, defaultValue) => {
const recur = (current, path) => {
if (current === undefined) {
return defaultValue;
}
if (path.length === 0) {
return current;
}
return recur(current[path[0]], path.slice(1));
};
return recur(object, path);
};
const set = (object, path, callback) => {
const setKey = (current, key, value) => {
if (Array.isArray(current)) {
return value === REMOVE
? current.filter((_, i) => key !== i)
: current.map((c, i) => (i === key ? value : c));
}
return value === REMOVE
? Object.entries(current).reduce((result, [k, v]) => {
if (k !== key) {
result[k] = v;
}
return result;
}, {})
: { ...current, [key]: value };
};
const recur = (current, path) => {
if (path.length === 1) {
return setKey(
current,
path[0],
callback(current[path[0]])
);
}
return setKey(
current,
path[0],
recur(current[path[0]], path.slice(1))
);
};
return recur(object, path, callback);
};
class App extends React.Component {
state = {
permission: { group: [] },
topGroup: [],
some: { other: [{ nested: { state: [] } }] },
};
permissionsKey = ['permission', 'group']; //where to find permissions in state
topGroupKey = ['topGroup']; //where to find top group in state
someKey = ['some', 'other', 0, 'nested', 'state']; //where other group is in state
onChange = (key, value) => {
//use set helper to set state
this.setState(set(this.state, key, arr => value));
};
isIndeterminate = () =>
!this.isEverythingChecked() &&
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result || get(this.state, key).length,
false
);
toggleEveryting = e => {
const checked = e.target.checked;
this.setState(
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
set(result, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
),
this.state
)
);
};
onToggleAll = (checked, key) => {
this.setState(
//use set helper to set state
set(this.state, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
)
);
};
isEverythingChecked = () =>
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result &&
get(this.state, key).length ===
this.plainOptions.length,
true
);
plainOptions = [
{ value: 1, name: 'Apple' },
{ value: 2, name: 'Pear' },
{ value: 3, name: 'Orange' },
];
render() {
return (
<React.Fragment>
<h1>App state</h1>
{JSON.stringify(this.state)}
<div>
<Checkbox
indeterminate={this.isIndeterminate()}
onChange={this.toggleEveryting}
checked={this.isEverythingChecked()}
>
Toggle everything
</Checkbox>
</div>
{[
{ label: 'group', stateKey: this.permissionsKey },
{ label: 'top', stateKey: this.topGroupKey },
{ label: 'other', stateKey: this.someKey },
].map(({ label, stateKey }) => (
<Row
key={label}
options={this.plainOptions}
// use getter to get state selected value
// for this particular group
checked={get(this.state, stateKey)}
label={label}
onChange={this.onChange} //change behaviour from App
onToggleAll={this.onToggleAll} //toggle all from App
//state key to indicate what state needs to change
// used in setState in App and passed to set helper
stateKey={stateKey}
/>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<link href="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.js"></script>
<div id="root"></div>
I rewrite all the handlers.
The bug in your code is located on the usage of antd Checkbox.Group component with map as a child component, perhaps we need some key to distinguish each of the Row. Simply put them in one component works without that strange state update.
As the demand during communication, the total button is also added.
And, we don't need many states, keep the single-source data is always the best practice.
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Checkbox } from "antd";
const group = ["group", "top"];
const groupItems = ["Apple", "Pear", "Orange"];
const CheckboxGroup = Checkbox.Group;
class App extends React.Component {
constructor() {
super();
this.state = {
permission: {}
};
}
UNSAFE_componentWillMount() {
this.setDefault(false);
}
setDefault = fill => {
const temp = {};
group.forEach(x => (temp[x] = fill ? groupItems : []));
this.setState({ permission: temp });
};
checkLength = () => {
const { permission } = this.state;
let sum = 0;
Object.keys(permission).forEach(x => (sum += permission[x].length));
return sum;
};
/**
* For total
*/
isTotalIndeterminate = () => {
const len = this.checkLength();
return len > 0 && len < groupItems.length * group.length;
};
onCheckTotalChange = () => e => {
this.setDefault(e.target.checked);
};
isTotalChecked = () => {
return this.checkLength() === groupItems.length * group.length;
};
/**
* For each group
*/
isIndeterminate = label => {
const { permission } = this.state;
return (
permission[label].length > 0 &&
permission[label].length < groupItems.length
);
};
onCheckAllChange = label => e => {
const { permission } = this.state;
const list = e.target.checked ? groupItems : [];
this.setState({ permission: { ...permission, [label]: list } });
};
isAllChecked = label => {
const { permission } = this.state;
return !groupItems.some(x => !permission[label].includes(x));
};
/**
* For each item
*/
isChecked = label => {
const { permission } = this.state;
return permission[label];
};
onChange = label => e => {
const { permission } = this.state;
this.setState({ permission: { ...permission, [label]: e } });
};
render() {
const { permission } = this.state;
console.log(permission);
return (
<React.Fragment>
<Checkbox
indeterminate={this.isTotalIndeterminate()}
onChange={this.onCheckTotalChange()}
checked={this.isTotalChecked()}
>
Check all
</Checkbox>
{group.map(label => (
<div key={label}>
<div className="site-checkbox-all-wrapper">
<Checkbox
indeterminate={this.isIndeterminate(label)}
onChange={this.onCheckAllChange(label)}
checked={this.isAllChecked(label)}
>
Check all
</Checkbox>
<CheckboxGroup
options={groupItems}
value={this.isChecked(label)}
onChange={this.onChange(label)}
/>
</div>
</div>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
Try it online:
Please try this,
onChange = value => checked => {
this.setState({ checked }, () => {
this.setState(prevState => {
permission : { ...prevSatate.permission , { [value]: this.state.checked }}
});
});
};
by using spread operator you can stop mutating the object. same way you can also use object.assign like this.
this.setState(prevState => {
permission : Object.assign({} , prevState.permission, { [value]: this.state.checked });
});
And also i would suggest not to call setState in a callback. If you want to access the current state you can simply use the current checked value which you are getting in the function itself.
so your function becomes ,
onChange = value => checked => {
this.setState({ checked });
this.setState(prevState => {return { permission : { ...prevSatate.permission, { [value]: checked }}
}});
};
Try the following
//Inside constructor do the following
this.state = {checkState:[]}
this.setChecked = this.setChecked.bind(this);
//this.setChecked2 = this.setChecked2.bind(this);
//Outside constructor but before render()
setChecked(e){
this.setState({
checkState : this.state.checkState.concat([{checked: e.target.id + '=>' + e.target.value}])
//Id is the id property for a specific(target) field
});
}
//Finally attack the method above.i.e. this.setChecked to a form input.
Hope it will address your issues

Return multiple HOC components from Array in React JS

I am trying to declare and return multiple HOC's from any array, but keep being returned a "Functions are not valid as a React child." Error. Has anyone ran into this issue before?
JS:
....
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
const withRequestAnimationFrame = () => WrappedComponent => {
class RequestAnimationFrame extends Component {
state = {
timeStamp: 0,
newItem: "Test"
};
componentDidMount() {
const min = 1;
const max = 100;
const rand = Math.floor(Math.random() * (max - min + 1)) + min;
this.setState({ timeStamp: this.state.timeStamp + rand });
}
render() {
return (
<WrappedComponent {...this.state} {...this.props} />
)
}
}
return RequestAnimationFrame;
};
const App = ({ timeStamp, newItem }) => (
<div style={styles}>
<h1>{timeStamp}</h1>
<p>{newItem}</p>
</div>
);
const arrayItems = ["EnhancedApp", "EnhancedApp2"];
const Products = ({ items }) => {
return (
items.map((item, index) => (
item = withRequestAnimationFrame()(App)
))
)
};
function Product() {
return (
<div>
<Products items={arrayItems} />
</div>
)
}
render(<Product />, document.getElementById("root"));
This line is the problem:
item = withRequestAnimationFrame()(App)
What your doing there is assigning result of withRequestAnimationFrame()(App)
function to item which is definetly not what you wanted. I assume you wanted to
render there multiple instances of withRequestAnimationFrame component. You can
do it like this:
items.map((item, index) => (
const NewComponent = withRequestAnimationFrame(item)(App);
return <NewComponent key={index}/>
))
Second problem is that you are not passing item prop to the wrapped component.
To pass item prop you should do:
const withRequestAnimationFrame = (item) => WrappedComponent => {
class RequestAnimationFrame extends React.Component {
state = {
timeStamp: 0,
newItem: item
};

Update nested array values using map and index es6

I have a react form which has dynamic rows, but I'm stuck at line 76 (Demo: https://codesandbox.io/s/pw58j0vzoq), not sure what can I do to update the value in the row
class App extends React.Component {
state = {
acceptedValues: [
{
id: 1,
_arguments: ["Samsung", "xiaomi"]
},
{
id: 2,
_arguments: ["OR", "AND"]
}
]
};
handleChange = (name, index, argumentIndex) => e => {
const { acceptedValues } = this.state;
if (name === "_arguments") {
updatedState = acceptedValues.map((o, i) => {
if (i === index) {
return {
...o,
_arguments: o._arguments.map((o2, index2) => {
if (index2 === argumentIndex) {
//what to do here?
}
return o;
})
};
}
return o;
});
this.setState({
acceptedValues: updatedState
});
}
};
render() {
const { acceptedValues } = this.state;
return (
<div>
{acceptedValues.map(({ operator, _arguments }, index) => (
<div style={{ marginBottom: 20 }}>
<div>
{_arguments.map((val, argumentIndex) => (
<div>
<input
onChange={this.handleChange(
"_arguments",
index,
argumentIndex
)}
id="_arguments"
type="text"
value={val}
/>
<button onClick={this.removeArgument(index, argumentIndex)}>
-
</button>
</div>
))}
</div>
</div>
))}
</div>
);
}
}
I'm able to navigate until the correct array but stuck at how to update the value within the array, I've 2 indexs in my handleChange function.
There you are mapping an array into another, so you should just pick which string to return, like this:
_arguments: o._arguments.map(
(o2, index2) => {
if (index2 === argumentIndex) return e.target.value
return o2
}
)
Or in an even cleaner way:
_arguments: o._arguments.map(
(o2, index2) => index2 === argumentIndex ? e.target.value : o2
)

How do I pass children index number to a react component?

My Data looks like the below.
const data = {
main: {
88: {
issues: [
{
id: 1
},
{
id: 3
},
{
id: 4
}
]
}
}
};
I am looping through data.main and then passing some data to the child component like this.
{Object.values(data.main).map((key) => {
<Issues
id={key}
issueIndex={XXX}
{...this.props}
/>
}
But I also want to pass an index of all issues to the child element. So that I can number it from here.
My attemp inside jsx file below.
{Object.values(data.main).map((group, key) => {
let issuesArr = group.issues;
{issuesArr.map((value, index) => { index + 1; })}
<Issue
id={key}
issueIndex={XXX}
{...this.props}
/>
}
I want to pass a number as issueIndex to the <Issue /> I have no control of the data structure.
That is happening because your issuesArr.map isn't doing anything. Change your code to this:
{
Object.values(data.main).map((group, key) => {
let issuesArr = group.issues;
{
issuesArr.map((value, index) => {
return (
<Issue
id={key}
issueIndex={value.id}
{...this.props}
/>
)
}
}
}

Categories

Resources