React Hook:将数据从子组件发送到父组件
React Hook : Send data from child to parent component
我正在寻找将数据从子组件传递到父组件的最简单的解决方案。
我听说过使用 Context,通过属性传递或更新 props,但我不知道哪个是最好的解决方案。
我正在构建一个管理界面,其中的 PageComponent 包含一个带有 table 的 ChildComponent,我可以在其中 select 多行。我想将我在 ChildComponent 中 select 编辑的行数发送给我的父 PageComponent。
类似的东西:
页面组件:
<div className="App">
<EnhancedTable />
<h2>count 0</h2>
(count should be updated from child)
</div>
子组件:
const EnhancedTable = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Click me {count}
</button>
)
};
我确信这是一件非常简单的事情,我不想为此使用 redux。
这些情况的常用技术是 lift the state up 到所有需要使用状态的组件的第一个共同祖先(即本例中的 PageComponent
)并向下传递状态和状态改变函数作为 props 传递给子组件。
例子
const { useState } = React;
function PageComponent() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1)
}
return (
<div className="App">
<ChildComponent onClick={increment} count={count} />
<h2>count {count}</h2>
(count should be updated from child)
</div>
);
}
const ChildComponent = ({ onClick, count }) => {
return (
<button onClick={onClick}>
Click me {count}
</button>
)
};
ReactDOM.render(<PageComponent />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
您可以在父组件中创建一个方法,将其传递给子组件,并在每次子状态发生变化时从 props 中调用它,将状态保持在子组件中。
const EnhancedTable = ({ parentCallback }) => {
const [count, setCount] = useState(0);
return (
<button onClick={() => {
const newValue = count + 1;
setCount(newValue);
parentCallback(newValue);
}}>
Click me {count}
</button>
)
};
class PageComponent extends React.Component {
callback = (count) => {
// do something with value in parent component, like save to state
}
render() {
return (
<div className="App">
<EnhancedTable parentCallback={this.callback} />
<h2>count 0</h2>
(count should be updated from child)
</div>
)
}
}
为了让事情变得超级简单,您实际上可以将状态 setter 共享给子级,现在他们可以设置其父级的状态。
示例:
假设有以下4个组件,
function App() {
return (
<div className="App">
<GrandParent />
</div>
);
}
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
return (
<>
<div>{name}</div>
<Parent setName={setName} />
</>
);
};
const Parent = params => {
return (
<>
<button onClick={() => params.setName("i'm from Parent")}>
from Parent
</button>
<Child setName={params.setName} />
</>
);
};
const Child = params => {
return (
<>
<button onClick={() => params.setName("i'm from Child")}>
from Child
</button>
</>
);
};
因此祖父组件具有实际状态,通过将 setter 方法 (setName) 共享给父子组件,他们可以获得更改祖父组件状态的权限。
您可以在下面的沙箱中找到工作代码,
https://codesandbox.io/embed/async-fire-kl197
我不得不处理类似的问题,并找到了另一种方法,使用对象来引用不同函数之间的状态,并且在同一个文件中。
import React, { useState } from "react";
let myState = {};
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
myState.name=name;
myState.setName=setName;
return (
<>
<div>{name}</div>
<Parent />
</>
);
};
export default GrandParent;
const Parent = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Parent")}>
from Parent
</button>
<Child />
</>
);
};
const Child = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Child")}>
from Child
</button>
</>
);
};
我必须在 键入脚本 中执行此操作。面向对象方面需要开发者在继承parent后在接口中添加这个回调方法作为一个字段,这个prop的类型为Function。我发现这很酷!
这是我们如何将状态直接传递给父级的另一个示例。
我修改了 react-select 库中的一个组件示例,它是一个 CreatableSelect 组件。该组件最初是作为基于 class 的组件开发的,我将其变成了功能组件并更改了状态操作算法。
import React, {KeyboardEventHandler} from 'react';
import CreatableSelect from 'react-select/creatable';
import { ActionMeta, OnChangeValue } from 'react-select';
const MultiSelectTextInput = (props) => {
const components = {
DropdownIndicator: null,
};
interface Option {
readonly label: string;
readonly value: string;
}
const createOption = (label: string) => ({
label,
value: label,
});
const handleChange = (value: OnChangeValue<Option, true>, actionMeta: ActionMeta<Option>) => {
console.group('Value Changed');
console.log(value);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
props.setValue(value);
};
const handleInputChange = (inputValue: string) => {
props.setInputValue(inputValue);
};
const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
if (!props.inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
console.group('Value Added');
console.log(props.value);
console.groupEnd();
props.setInputValue('');
props.setValue([...props.value, createOption(props.inputValue)])
event.preventDefault();
}
};
return (
<CreatableSelect
id={props.id}
instanceId={props.id}
className="w-100"
components={components}
inputValue={props.inputValue}
isClearable
isMulti
menuIsOpen={false}
onChange={handleChange}
onInputChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder="Type something and press enter..."
value={props.value}
/>
);
};
export default MultiSelectTextInput;
我从我的下一个 js 项目的页面中这样调用它
import MultiSelectTextInput from "../components/Form/MultiSelect/MultiSelectTextInput";
const NcciLite = () => {
const [value, setValue] = useState<any>([]);
const [inputValue, setInputValue] = useState<any>('');
return (
<React.Fragment>
....
<div className="d-inline-flex col-md-9">
<MultiSelectTextInput
id="codes"
value={value}
setValue={setValue}
inputValue={inputValue}
setInputValue={setInputValue}
/>
</div>
...
</React.Fragment>
);
};
如图所示,该组件修改了调用它的页面(父页面)的状态。
如果我们有父 Class 组件和子功能组件,这就是我们访问子组件 useStates 挂钩值的方式:--
class parent extends Component() {
constructor(props){
super(props)
this.ChildComponentRef = React.createRef()
}
render(){
console.log(' check child stateValue: ',
this.ChildComponentRef.current.info);
return (<> <ChildComponent ref={this.ChildComponentRef} /> </>)
}
}
我们将使用
创建的子组件
React.forwardRef((props, ref) => (<></>))
。和
useImperativeHandle(ref, createHandle, [deps])
自定义暴露给父组件的实例值
const childComponent = React.forwardRef((props, ref) => {
const [info, setInfo] = useState("")
useEffect(() => {
axios.get("someUrl").then((data)=>setInfo(data))
})
useImperativeHandle(ref, () => {
return {
info: info
}
})
return (<> <h2> Child Component <h2> </>)
})
我正在寻找将数据从子组件传递到父组件的最简单的解决方案。
我听说过使用 Context,通过属性传递或更新 props,但我不知道哪个是最好的解决方案。
我正在构建一个管理界面,其中的 PageComponent 包含一个带有 table 的 ChildComponent,我可以在其中 select 多行。我想将我在 ChildComponent 中 select 编辑的行数发送给我的父 PageComponent。
类似的东西:
页面组件:
<div className="App">
<EnhancedTable />
<h2>count 0</h2>
(count should be updated from child)
</div>
子组件:
const EnhancedTable = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Click me {count}
</button>
)
};
我确信这是一件非常简单的事情,我不想为此使用 redux。
这些情况的常用技术是 lift the state up 到所有需要使用状态的组件的第一个共同祖先(即本例中的 PageComponent
)并向下传递状态和状态改变函数作为 props 传递给子组件。
例子
const { useState } = React;
function PageComponent() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1)
}
return (
<div className="App">
<ChildComponent onClick={increment} count={count} />
<h2>count {count}</h2>
(count should be updated from child)
</div>
);
}
const ChildComponent = ({ onClick, count }) => {
return (
<button onClick={onClick}>
Click me {count}
</button>
)
};
ReactDOM.render(<PageComponent />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
您可以在父组件中创建一个方法,将其传递给子组件,并在每次子状态发生变化时从 props 中调用它,将状态保持在子组件中。
const EnhancedTable = ({ parentCallback }) => {
const [count, setCount] = useState(0);
return (
<button onClick={() => {
const newValue = count + 1;
setCount(newValue);
parentCallback(newValue);
}}>
Click me {count}
</button>
)
};
class PageComponent extends React.Component {
callback = (count) => {
// do something with value in parent component, like save to state
}
render() {
return (
<div className="App">
<EnhancedTable parentCallback={this.callback} />
<h2>count 0</h2>
(count should be updated from child)
</div>
)
}
}
为了让事情变得超级简单,您实际上可以将状态 setter 共享给子级,现在他们可以设置其父级的状态。
示例: 假设有以下4个组件,
function App() {
return (
<div className="App">
<GrandParent />
</div>
);
}
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
return (
<>
<div>{name}</div>
<Parent setName={setName} />
</>
);
};
const Parent = params => {
return (
<>
<button onClick={() => params.setName("i'm from Parent")}>
from Parent
</button>
<Child setName={params.setName} />
</>
);
};
const Child = params => {
return (
<>
<button onClick={() => params.setName("i'm from Child")}>
from Child
</button>
</>
);
};
因此祖父组件具有实际状态,通过将 setter 方法 (setName) 共享给父子组件,他们可以获得更改祖父组件状态的权限。
您可以在下面的沙箱中找到工作代码, https://codesandbox.io/embed/async-fire-kl197
我不得不处理类似的问题,并找到了另一种方法,使用对象来引用不同函数之间的状态,并且在同一个文件中。
import React, { useState } from "react";
let myState = {};
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
myState.name=name;
myState.setName=setName;
return (
<>
<div>{name}</div>
<Parent />
</>
);
};
export default GrandParent;
const Parent = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Parent")}>
from Parent
</button>
<Child />
</>
);
};
const Child = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Child")}>
from Child
</button>
</>
);
};
我必须在 键入脚本 中执行此操作。面向对象方面需要开发者在继承parent后在接口中添加这个回调方法作为一个字段,这个prop的类型为Function。我发现这很酷!
这是我们如何将状态直接传递给父级的另一个示例。
我修改了 react-select 库中的一个组件示例,它是一个 CreatableSelect 组件。该组件最初是作为基于 class 的组件开发的,我将其变成了功能组件并更改了状态操作算法。
import React, {KeyboardEventHandler} from 'react';
import CreatableSelect from 'react-select/creatable';
import { ActionMeta, OnChangeValue } from 'react-select';
const MultiSelectTextInput = (props) => {
const components = {
DropdownIndicator: null,
};
interface Option {
readonly label: string;
readonly value: string;
}
const createOption = (label: string) => ({
label,
value: label,
});
const handleChange = (value: OnChangeValue<Option, true>, actionMeta: ActionMeta<Option>) => {
console.group('Value Changed');
console.log(value);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
props.setValue(value);
};
const handleInputChange = (inputValue: string) => {
props.setInputValue(inputValue);
};
const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
if (!props.inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
console.group('Value Added');
console.log(props.value);
console.groupEnd();
props.setInputValue('');
props.setValue([...props.value, createOption(props.inputValue)])
event.preventDefault();
}
};
return (
<CreatableSelect
id={props.id}
instanceId={props.id}
className="w-100"
components={components}
inputValue={props.inputValue}
isClearable
isMulti
menuIsOpen={false}
onChange={handleChange}
onInputChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder="Type something and press enter..."
value={props.value}
/>
);
};
export default MultiSelectTextInput;
我从我的下一个 js 项目的页面中这样调用它
import MultiSelectTextInput from "../components/Form/MultiSelect/MultiSelectTextInput";
const NcciLite = () => {
const [value, setValue] = useState<any>([]);
const [inputValue, setInputValue] = useState<any>('');
return (
<React.Fragment>
....
<div className="d-inline-flex col-md-9">
<MultiSelectTextInput
id="codes"
value={value}
setValue={setValue}
inputValue={inputValue}
setInputValue={setInputValue}
/>
</div>
...
</React.Fragment>
);
};
如图所示,该组件修改了调用它的页面(父页面)的状态。
如果我们有父 Class 组件和子功能组件,这就是我们访问子组件 useStates 挂钩值的方式:--
class parent extends Component() {
constructor(props){
super(props)
this.ChildComponentRef = React.createRef()
}
render(){
console.log(' check child stateValue: ',
this.ChildComponentRef.current.info);
return (<> <ChildComponent ref={this.ChildComponentRef} /> </>)
}
}
我们将使用
创建的子组件React.forwardRef((props, ref) => (<></>))
。和
useImperativeHandle(ref, createHandle, [deps])
自定义暴露给父组件的实例值
const childComponent = React.forwardRef((props, ref) => {
const [info, setInfo] = useState("")
useEffect(() => {
axios.get("someUrl").then((data)=>setInfo(data))
})
useImperativeHandle(ref, () => {
return {
info: info
}
})
return (<> <h2> Child Component <h2> </>)
})