我想出了两个解决方案,但他们都没有感觉到.
第一解决方案
state.input.onChange在P中创建一个函数,它接收一个事件并设置state.input.onChange给C1作为a props,让C1绑定this.props.onChange到onChangeFoo.这有效.每当Foo的值改变时,它会触发一个setStateP,所以P将输入传递给C2.
但由于同样的原因,它感觉不太正确:我正在从子元素设置父元素的状态.这似乎背叛了React的设计原则:单向数据流.
这是我应该怎么做的,还是有更多的React-natural解决方案?
二解决方案:
把Foo放在P.
但是,当我构建我的应用程序时,我应该遵循这个设计原则 - 将所有表单元素放入render最高级别的类中吗?
就像在我的例子中,如果我有一个大的C1渲染,我真的不想把整个renderC1放到renderP只是因为C1有一个表单元素.
我该怎么办?
cap*_*ray 190
所以,如果我正确理解你,你的第一个解决方案是建议你在你的根组件中保持状态?我不能代表React的创造者,但一般来说,我觉得这是一个合适的解决方案.
维持状态是React创建的原因之一(至少我认为).如果您曾经实现过自己的状态模式客户端来处理具有大量相互依赖的移动块的动态UI,那么您会喜欢React,因为它可以减轻很多状态管理的痛苦.
通过将状态保持在层次结构中并通过事件更新它,您的数据流仍然是单向的,您只是响应Root组件中的事件,您实际上并不是通过双向绑定获取数据,你告诉Root组件"嘿,这里发生的事情,检查值"或者你正在传递子组件中某些数据的状态以更新状态.您在C1中更改了状态,并且希望C2知道它,因此,通过更新Root组件中的状态并重新渲染,C2的props现在处于同步状态,因为状态在Root组件中更新并传递.
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
onUpdate (data) { this.setState({ data }) }
}
class C1 extends React.Component {
render () {
return (
<div>
<input type='text' ref='myInput'/>
<input type='button' onClick={this.update.bind(this)} value='Update C2'/>
</div>
)
}
update () {
this.props.onUpdate(this.refs.myInput.getDOMNode().value)
}
})
class C2 extends React.Component {
render () {
return <div>{this.props.data}</div>
}
})
ReactDOM.renderComponent(<Example/>, document.body)
Run Code Online (Sandbox Code Playgroud)
oct*_*ref 34
现在使用React构建应用程序,我想与我半年前提出的这个问题分享一些想法.
我建议你阅读
第一篇文章非常有助于了解如何构建React应用程序.
Flux回答了为什么要以这种方式构建React应用程序的问题(而不是如何构建它).React只占系统的50%,通过Flux,您可以看到整个画面,看看它们是如何构成一个连贯的系统.
回到问题.
至于我的第一个解决方案,让处理程序反向运行是完全可以的,因为数据仍然是单向的.
但是,根据您的情况,是否允许处理程序在P中触发setState可能是对还是错.
如果应用程序是一个简单的Markdown转换器,C1是原始输入,C2是HTML输出,可以让C1在P中触发setState,但有些人可能认为这不是推荐的方法.
但是,如果应用程序是todo列表,C1是用于创建新待办事项的输入,C2是HTML中的待办事项列表,您可能希望处理程序比P更高两级dispatcher,以便store更新data store,然后将数据发送到P并填充视图.看到Flux文章.这是一个例子:Flux - TodoMVC
通常,我更喜欢todo列表示例中描述的方式.您在应用中的状态越少越好.
五年后,随着 React Hooks 的推出,现在有了更优雅的使用 useContext hook 的方法。
您在全局范围内定义上下文,在父组件中导出变量、对象和函数,然后将应用程序中的子组件包装在提供的上下文中,并在子组件中导入您需要的任何内容。下面是一个概念证明。
import React, { useState, useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.css";
// Create context container in a global scope so it can be visible by every component
const ContextContainer = React.createContext(null);
const initialAppState = {
selected: "Nothing"
};
function App() {
// The app has a state variable and update handler
const [appState, updateAppState] = useState(initialAppState);
return (
<div>
<h1>Passing state between components</h1>
{/*
This is a context provider. We wrap in it any children that might want to access
App's variables.
In 'value' you can pass as many objects, functions as you want.
We wanna share appState and its handler with child components,
*/}
<ContextContainer.Provider value={{ appState, updateAppState }}>
{/* Here we load some child components */}
<Book title="GoT" price="10" />
<DebugNotice />
</ContextContainer.Provider>
</div>
);
}
// Child component Book
function Book(props) {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// In this child we need the appState and the update handler
const { appState, updateAppState } = useContext(ContextContainer);
function handleCommentChange(e) {
//Here on button click we call updateAppState as we would normally do in the App
// It adds/updates comment property with input value to the appState
updateAppState({ ...appState, comment: e.target.value });
}
return (
<div className="book">
<h2>{props.title}</h2>
<p>${props.price}</p>
<input
type="text"
//Controlled Component. Value is reverse vound the value of the variable in state
value={appState.comment}
onChange={handleCommentChange}
/>
<br />
<button
type="button"
// Here on button click we call updateAppState as we would normally do in the app
onClick={() => updateAppState({ ...appState, selected: props.title })}
>
Select This Book
</button>
</div>
);
}
// Just another child component
function DebugNotice() {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// but in this child we only need the appState to display its value
const { appState } = useContext(ContextContainer);
/* Here we pretty print the current state of the appState */
return (
<div className="state">
<h2>appState</h2>
<pre>{JSON.stringify(appState, null, 2)}</pre>
</div>
);
}
const rootElement = document.body;
ReactDOM.render(<App />, rootElement);
Run Code Online (Sandbox Code Playgroud)
您可以在代码沙盒编辑器中运行此示例。
我很惊讶在我写的那一刻,没有一个简单的惯用 React 解决方案的答案。所以这是一个(将大小和复杂性与其他人进行比较):
class P extends React.Component {
state = { foo : "" };
render(){
const { foo } = this.state;
return (
<div>
<C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
<C2 value={ foo } />
</div>
)
}
}
const C1 = ({ value, onChange }) => (
<input type="text"
value={ value }
onChange={ e => onChange( e.target.value ) } />
);
const C2 = ({ value }) => (
<div>Reacting on value change: { value }</div>
);
Run Code Online (Sandbox Code Playgroud)
我正在从子元素设置父元素的状态。这似乎违背了 React 的设计原则:单向数据流。
任何受控input(在 React 中使用表单的惯用方式)都会在其onChange回调中更新父状态,并且仍然不会泄露任何内容。
例如,仔细查看 C1 组件。您是否看到C1内置input组件和内置组件处理状态变化的方式有什么显着差异?你不应该,因为没有。提升状态并传递 value/onChange 对是原始 React 的惯用方式。正如一些答案所暗示的那样,不使用 refs。
| 归档时间: |
|
| 查看次数: |
121629 次 |
| 最近记录: |