I can't solve this case.
It's from parent
<ChildDiv key={el} width={i} value={' '}></ChildDiv>
It's from Child
<MyContext.Consumer>
{context => (
<div onClick={() => test(value, context.ChangeTurn)} className="row">
</div>
)}
</MyContext.Consumer>
let test = (value, context) => {
if (value !== "X" && val !== "O") {
value = "laslda";
}};
So I want change value from local function.
If you want context consumers to be able to update the context, it's better to expose a method within the context to perform the update; the component provider can then pass a new context object to consumers, provoking a re-render.
Here's what the code could look like:
Context (./context.js):
import { createContext } from 'react';
const Context = createContext({ value: null, setValue: () => {} });
export default Context;
Provider (./provider.js):
import { useState } from 'react';
import Context from './context';
const Provider = ({ value: startingValue, children }) => {
const [value, setValue] = useState(startingValue);
return (
<Context.Provider value={{ value, setValue }}>
{children}
</Context.Provider>
);
};
Sample consumer (./consumer.js):
import Context from './context';
export default Context.Consumer;
Now you can do this at your root (./app.js):
import Provider from './provider';
import ChildComponent from './child-component';
export default function App() {
return (
<Provider value={0}>
<ChildComponent/>
</Provider>
);
}
...and this in child components (using hooks, ./child-component.js):
import { useContext } from 'react';
import Context from './context';
export default function ChildComponent() {
const { value, setValue } = useContext(Context);
return (
<div>
<h1>Counter: {value}</h1>
<button onClick={e => { e.preventDefault(); setValue(value - 1) }}>-</button>
<button onClick={e => { e.preventDefault(); setValue(value + 1) }}>+</button>
</div>
);
}
...or without hooks:
import React from 'react';
import Consumer from './consumer';
export default function ChildComponent() {
return (
<Consumer>
({ value, setValue }) => (
<div>
<h1>Counter: {value}</h1>
<button onClick={e => { e.preventDefault(); setValue(value - 1) }}>-</button>
<button onClick={e => { e.preventDefault(); setValue(value + 1) }}>+</button>
</div>
);
</Consumer>
);
}
In react, props like the value you passing, are Read-Only.
Remember that props are readonly. They should not be modified in any way.
You may want to provide a setter:
<MyContext.Provider value={{ dispatch: this.setState }}>
<ChildDiv key={el} width={i} value={value}></ChildDiv>
</MyContext.Provider>
<MyContext.Consumer>
{({ dispatch }) => (
<button onClick={() => dispatch({value: 'New Value'})}>Button</button>
)}
</MyContext.Consumer>
Related
I have two components, the parent and child. Currently I have these codes below. But unfortunately it returns an error:
TypeError: Cannot read property 'click' of null
For some reasons I want when button is click the Item component also will be click. But these codes below produces an error above. Anyone does know how to achieve it?
import React, { useRef } from 'react';
const App = (props) => {
const itemRef = useRef(null);
return (
<div>
{dynamicBoolean ? (
<button onClick={() => itemRef.current.click()}>
click item
</button>
) : (
//more codes here
<Item ref={itemRef} />
)}
</div>
);
};
export default App;
Child component would look like below (demonstration purposes, the code is very lengthly)
import React from 'react';
const Item = (props) => {
return (
<div>
//some design here
</div>
);
};
export default Item;
You need useRef and you have to forward this ref to the Item component.
import React, { forwardRef, useRef } from 'react';
const Item = forwardRef((props, ref) => {
return <li {...props}
onClick={() => alert('clicked on Item')}
ref={ref} >MyItem</li>
})
const App = (props) => {
const itemRef = useRef(null);
return (
<div>
<button onClick={() => itemRef.current.click()}>
click item
</button>
<Item ref={itemRef} />
</div>
);
};
export default App;
import React, { createRef } from "react";
const Hello = (props) => {
const itemRef = createRef();
const hello = () => {
itemRef.current.click();
};
return (
<div>
<button onClick={() => hello()}>click item</button>
<Item ref={itemRef} />
</div>
);
};
const Item = React.forwardRef((props, ref) => {
const myClick = () => {
console.log("this is clicked");
};
return (
<button ref={ref} className="FancyButton" onClick={myClick}>
{props.children}
</button>
);
});
export default Hello;
I have this structure
component 1
import React, { useState } from 'react'
export default function Component1() {
return (
<div>
<button onClick={handleChange}></button>
</div>
)
}
component 2
import React, { useState } from 'react'
export default function Component2() {
return (
<div>
<button onClick={handleChange}></button>
</div>
)
}
and the parent
import React from 'react'
export default function Parent() {
return (
<div>
<Component1 />
<Component2 />
</div>
)
}
The question is, how can I toggle visibility between the two, without having a button in the parent. Just the buttons inside each component. - The Component1 should be visible by default and when you press the button in Component1 it will hide it and show Component2 and vice-versa.
I've tried using useState hook on the Component1 button, but I'm not sure how to export the state and add it to the parent component.
const [showMini, setShowMini] = useState(false);
const handleChange = () => {
setShowMini(true);
}
Is this possible? or it's possible just with a button in the parent that control the two?
Thanks
Try this:
import React from 'react'
export default function Parent() {
const[show,setShow]=useState(false);
const handleChange=()=>{
setShow(!show);
}
return (
<div>
{show ? <Component2 handleChange={handleChange}/> : <Component1 handleChange={handleChange}/>}
</div>
)
}
and inside Component1 have this:
import React, { useState } from 'react'
export default function Component1({handleChange}) {
return (
<div>
<button onClick={handleChange}></button>
</div>
)
}
Similarly do it for Component2
You can do with state value and pass handleChange function ad props in the child component and in click on the button in child component call handleChange method under parent component and show hide based on state value.
import React from 'react'
const [showChild, setshowChild] = useState(false);
const handleChange = () => {
setshowChild(!showChild);
}
export default function Parent() {
return (
<div>
{showChild ? <Component2 handleChange = {handleChange}/> : <Component1 handleChange= {handleChange} />}
</div>
)
}
You can manage the state in the parent and pass down a handler to the children
import React, { useState } from 'react'
const [currentView, setCurrentView] = useState('component1')
const changeCurrentView = (view) => setCurrentView(view)
const renderViews = () => {
switch(currentView) {
case 'component1':
return <Component1 changeCurrentView={changeCurrentView} />
case 'component2':
return <Component2 changeCurrentView={changeCurrentView} />
default:
return <Component1 changeCurrentView={changeCurrentView} />
}
}
export default function Parent() {
return (
<div>
{renderViews()}
</div>
)
}
Other components
import React from 'react'
export default function Component1({ changeCurrentView }) {
return (
<div>
<button onClick={() => changeCurrentView('component1')}></button>
</div>
)
}
export default function Component2({ changeCurrentView }) {
return (
<div>
<button onClick={() => changeCurrentView('component2')}></button>
</div>
)
}
Your parent component should keep track of the state:
import React, {useState} from 'react'
export default function Parent() {
const [showChild, setShowChild] = useState(1);
const showNextChild = () => {
setShowChild( showChild === 1 ? 2 : 1 ); // set to 2 if already 1, else to 1
}
return (
<div>
{ showChild === 1 && <Component1 handleChange={showNextChild} /> }
{ showChild === 2 && <Component2 handleChange={showNextChild} /> }
</div>
)
}
A few notes:
Your components are identical, so the duplication is unnecessary, but I assume the example is just contrived.
This assumes toggling 2 components back and forth. If you have more than 2 components you are "looping" through, you can instead increment the previous showChild state and then reset it to 0 if higher than the # of components you have.
The syntax you see, showChild === 1 && <Component1 ... uses the behavior of the && operator which actually returns the 2nd item it is evaluating if both are true. In other words, const isTrue = foo && bar; sets isTrue to bar, not true as you might expect. (You know, however, that bar is "truthy" in this case, so isTrue still works in future if statements and such.) The component is always truthy, so the effect is that the component is returned if the first part is true, otherwise it is not. It's a good trick for conditionally showing components.
Try this. You can send information from child to parent with functions passed as a prop.
Parent Component:
const Parent = () => {
const [show, setShow] = useState(true);
const toggleVisibility = () => {
setShow(!show);
};
return (
<div>
{show ? (
<Child1 toggle={toggleVisibility}></Child1>
) : (
<Child2 toggle={toggleVisibility}></Child2>
)}
</div>
);
};
Child 1
const Child1 = (props) => {
const { toggle } = props;
return (
<div style={{ width: '100px', height: '100px' }}>
<button onClick={toggle}>Child 1's button</button>
</div>
);
};
Child 2
const Child2 = (props) => {
const { toggle } = props;
return (
<div style={{ width: '100px', height: '100px' }}>
<button onClick={toggle}>Child 2's button</button>
</div>
);
};
I have a button in a child component AddMore.js which adds more items on a list, but it's not re-rendering the other child component that renders that list Pig.js. I thought my setup would work because I set up the context and provider in a similar fashion using the NextJS framework. Here's the source code, using codesandbox, or you can look at the code below.
App.js
import React, { useState } from "react";
import "./App.css";
import PigContext from "./store/PigContext";
import Pig from "./Pig";
import AddMore from "./AddMore";
function App() {
const [data, setData] = useState([
{ pig: "oink" },
{ pig: "bork" },
{ pig: "oinkoink" }
]);
return (
<div className="App">
<header className="App-header">
<PigContext.Provider value={{ data, setData }}>
<Pig />
<AddMore />
</PigContext.Provider>
</header>
</div>
);
}
export default App;
PigContext.js
import { createContext } from "react";
const PigContext = createContext(undefined);
export default PigContext;
Pig.js
import React, { useContext } from "react";
import PigContext from "./store/PigContext";
const Pig = () => {
const { data } = useContext(PigContext);
return (
<div>
{data.map(({ pig }, idx) => (
<div key={idx}>{pig}</div>
))}
</div>
);
};
export default Pig;
AddMore.js
import React, { useContext } from "react";
import PigContext from "./store/PigContext";
export default function AddMore() {
const { setData } = useContext(PigContext);
return (
<div>
<button
onClick={() =>
setData(prev => {
prev.push({ pig: "zoink" });
console.log(prev);
return prev;
})
}
>
add pig question
</button>
</div>
);
}
You are mutating the state object by calling push, you should return a new array instead in order to get the component updated.
AddMore.js:
...
<button
onClick={() =>
setData(prev => (
[...prev, ({ pig: "zoink" })]
))
}
>
...
It is because you are a mutating state in setData using push. using a method that doesn't mutate state like concat can fix the issue. Your AddMore function will look like
function AddMore() {
const { setData } = useContext(PigContext);
return (
<div>
<button onClick={() => setData(prev => prev.concat({ pig: "zoink" }))}>
add pig question
</button>
</div>
);
}
I have a top level context Provider, followed by a Parent class component follow by a functional stateless Child.
I can update the my context value from the Child, but not from the parent, even though the value updates in the parent.
How can I update and share state between both components using context?
import React from "react";
import ReactDOM from "react-dom";
const Context = React.createContext();
const Provider = ({ children }) => {
const [value, setValue] = React.useState(0);
return (
<Context.Provider value={{ value, setValue }}>{children}</Context.Provider>
);
};
const Child = () => {
const { value, setValue } = React.useContext(Context);
return <div onClick={() => setValue(value + 1)}>Plus plus!!</div>;
};
class Parent extends React.Component {
render() {
const { value, setValue } = this.context;
return (
<div>
<div onPress={() => setValue(value - 1)}>MINUS MINUS!</div>
<div>{this.props.children}</div>
<h1>{value}</h1>
</div>
);
}
}
Parent.contextType = Context;
function App() {
return (
<Provider>
<Parent>
<Child />
</Parent>
</Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
https://codesandbox.io/s/thirsty-oskar-ocmxr
Change the "onPress" to "onClick" will work. I have tested it.
I'm digging into my first react/redux application and I've been having quite a bit of trouble mapping my dispatch actions to onClick events in my components.
I've tried a couple of variations of trying to bind the onClick Event to the dispatch, but I always end up with either :
ReferenceError: onMovieClick is not defined
or alternatively when I do end up binding a function correctly I'll get an error related to dispatch is not defined.
My Goal
I'm trying to implement a filter(delete) from store function
actions/movieActions.js
import * as actionTypes from './actionTypes'
export const createMovie = (movie) => {
return {
type: actionTypes.CREATE_MOVIE,
movie
}
};
export const deleteMovie = (id) => {
console.log('action triggered. movie index:' + id)
return {
type: actionTypes.DELETE_MOVIE,
id
}
}
reducers/movieReducers.js
export default (state = [], action) => {
switch (action.type){
case 'CREATE_MOVIE':
return [
...state,
Object.assign({}, action.movie)
];
case 'DELETE_MOVIE':
return [
state.filter(({ id }) => id !== action.id)
]
default:
return state;
}
};
components/MovieList.js
import React from 'react'
import Slider from 'react-slick'
import { dispatch, connect } from 'react-redux'
import {Icon} from 'react-fa'
import { deleteMovie } from '../../actions/movieActions'
import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'
import './MovieList.scss'
class MovieList extends React.Component{
constructor(props){
super (props)
}
handleClick(id) {
dispatch(deleteMovie(id))
}
onMovieClick(id){
dispatch.deleteMovie(id)
}
render () {
// Settings for slick-carousel
let settings = {
infinite: true,
speed: 500
}
return (
<div className='col-lg-12'>
{this.props.movies.map((b, i) =>
<div key={i} className="col-lg-2">
<Slider {...settings}>
{b.images.map((b, z) =>
<div className="img-wrapper">
<Icon name="trash" className="trash-icon" onClick={() =>
console.log(this.props.movies[i].id),
onMovieClick(this.props.movies[i].id)
}/>
<img className="img-responsive" key={z} src={b.base64}></img>
</div>
)}
</Slider>
<div className="text-left info">
<h2>{b.title}</h2>
<p>{b.genre}</p>
</div>
</div>
)}
</div>
)
}
}
// map state from store to props
const mapStateToProps = (state) => {
return {
movies: state.movies
}
};
// Map actions to props
const mapDispatchToProps = (dispatch) => {
return {
onMovieClick: (id) => {
dispatch(deleteMovie(id))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MovieList)
Would love some advice if anyone has a moment.
Since you are passing onMovieClick through connect, you can actually invoke it from the MovieList component props. First, I would remove the onMovieClick method definition in your MovieList component and then use this.props.onMovieClick in the onclick handler of Icon like so:
<Icon name="trash" className="trash-icon" onClick={() =>
console.log(this.props.movies[i].id),
this.props.onMovieClick(this.props.movies[i].id)
}/>