gpt4 book ai didi

reactjs - 当 store 状态改变时重新渲染组件

转载 作者:行者123 更新时间:2023-12-04 11:16:35 24 4
gpt4 key购买 nike

我被这个问题困住了,我正在使用 redux 来解决这个问题,并将问题分为 4 个部分。我想要实现的是将组件 Prop 与另一个组件(也称为 PropEditor 表单)内的 UI 动态映射。我在说什么,首先看到它尚未实现,但它只是我想要实现的原型(prototype)。

PropEditor Prototype

如果您为我提供更好的解决方案来解决这个问题,我也将不胜感激。

我的方法:

我有一个名为 Heading.js 的组件其中包含 2 个 Prop hasFruit一个 bool 类型和一个 fruitName字符串类型。它可以是任何库中的组件,但让我们从简单开始。

src/components/Heading.js

import React from 'react';

export const Heading = (props) => {
const { hasFruit, fruitName } = props;
return <h1>Fruit name will show { hasFruit ? fruitName : 'Oh no!'}</h1>
};

A 部分:输入类型

我想在 PropEditor 上将此组件 Prop 显示为 UI成分。所以,我必须为 Prop 定义不同的 UI 组件。因此,我创建了 2 个输入类型组件。

src/editor/components/types/Boolean.js
import React from 'react';
import PropTypes from 'prop-types';


const propTypes = {
/** object for the boolean input type. */
prop: PropTypes.shape({
/** It will be the name of the prop. */
name: PropTypes.string,
/** It will be the value of the prop. */
value: PropTypes.bool,
}),
/** onChange handler for the input */
onChange: PropTypes.func
};

const defaultProps = {
prop: {},
onChange: (value) => value,
};



const Boolean = (props) => {

const { prop, onChange } = props;

return (
<input
id={prop.name}
name={prop.name}
type="checkbox"
onChange={(event) => onChange(event.target.checked)}
checked={prop.value}
/>
);

};


Boolean.propTypes = propTypes;
Boolean.defaultProps = defaultProps;

export default Boolean;

src/editor/components/types/Text.js
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
/** object for the text input type. */
prop: PropTypes.shape({
/** It will be the name of the prop. */
name: PropTypes.string,
/** It will be the value of the prop. */
value: PropTypes.string
}),
/** onChange handler for the input */
onChange: PropTypes.func
};

const defaultProps = {
prop: {},
onChange: (value) => value,
};



const Text = (props) => {

const { prop, onChange } = props;

const handleChange = (event) => {
const { value } = event.target;
onChange(value);
};


return (
<input
id={prop.name}
type="text"
onChange={handleChange}
value={prop.value}
/>
);

};


Text.propTypes = propTypes;
Text.defaultProps = defaultProps;

export default Text;

稍后我们将在 PropForm 中导入这些组件组件是 PropEditor 的子组件成分。所以我们可以映射这些类型。

src/editor/components/types/index.js
import BooleanType from './Boolean';
import TextType from './Text';

export default {
boolean: BooleanType,
text: TextType,
};

B 部分:Redux

整个场景,2 个 Action 将调度 SET_PROP在商店和 SET_PROP_VALUE 上设置 Prop 数据即通过 PropEditor 调度组件在输入更改时更新输入的值。

源代码/编辑器/操作类型:
// PropEditor Actions

// One single prop
export const SET_PROP = 'SET_PROP';

// One single prop value
export const SET_PROP_VALUE = 'SET_PROP_VALUE';

我定义了 2 个 Action 创建者。

src/editor/PropActions.js:
import * as actionTypes from './actionTypes';

// Prop related action creators
/**
* @param prop {Object} - The prop object
* @return {{type: {string}, data: {Object}}}
*/
export const setProp = (prop) => {
return {
type: actionTypes.SET_PROP,
data: prop
};
};


// Prop value related actions
/**
* @param prop {Object} - The prop object
* @return {{type: {string}, data: {Object}}}
*/
export const setPropValue = (prop) => {
return {
type: actionTypes.SET_PROP_VALUE,
data: prop
};
};

src/editor/PropReducer.js:
import * as actionTypes from './actionTypes';

const INITIAL_STATE = {};

export const propReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
// Prop Actions
case (actionTypes.SET_PROP):
const { data } = action;
return { ...state, [data.name]: {...data} };

// Prop Value Actions
case (actionTypes.SET_PROP_VALUE):
return { ...state, [action.data.name]: { ...state[action.data.name], value: action.data.value } };
default:
return state;
}
};

src/editor/PropStore.js:
import { createStore } from 'redux';
import { propReducer } from './PropReducer';

const REDUX_DEV_TOOL = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__();

export const store = createStore(propReducer, REDUX_DEV_TOOL);

引导我们的整个 Appreact-redux DOM 上的提供程序。

src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './editor/PropStore';
import App from './App';

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);

C部分:主体部分

如何映射组件 Heading.js带有 UI 的 Prop PropEditor成分?

对于这个用户必须用高阶组件包裹它的组件并在里面 HOC用户必须调用一些在幕后帮助我们动态填充商店的函数。我创建了一些函数,如 booleantext这将调度一个名为 SET_PROP 的 Action 填充商店状态。

src/editor/index.js
import { store } from './PropStore';
import { setProp } from './PropActions';

/**
* @param name {string} - The name of the prop
* @param options {Object} - The prop with some additional properties
* @return {*} - Returns the associated value of the prop
*/
const prop = (name, options) => {
const defaultValue = options.value;
// Create an object and merge with additional properties like `defaultValue`
const prop = {
...options,
name,
defaultValue,
};
store.dispatch(setProp(prop));
return defaultValue;
};

/**
* @param name {string} - The name of the prop
* @param value {boolean} - The value of the prop
* @return {boolean} - Returns the value of the prop
*/
export const boolean = (name, value) => {
// Returns the value of the prop
return prop(name, { type: 'boolean', value });
};

/**
* @param name {string} - The name of the prop
* @param value {string} - The value of the prop
* @return {text} - Returns the value of the prop
*/
export const text = (name, value) => {
// Returns the value of the prop
return prop(name, { type: 'text', value });
};

渲染 HOC组件和 PropEditor在 DOM 上:

src/blocks.js:
import React from 'react';
import { boolean, text } from './editor';
import { Heading } from './components/Heading';


// WithHeading Block
export const WithHeading = () => {
const boolVal = boolean('hasFruit', true);
const textVal = text('fruitName', 'Apple');
return (<Heading hasFruit={boolVal} fruitName={textVal}/>);
};

这是我们的主要 App成分。

src/App.js:
import React from 'react';
import { PropEditor } from './editor/components/PropEditor';
import { WithHeading } from './blocks';

const App = () => {
return (
<div className="App">
{/* PropEditor */}
<PropEditor />
{/* Blocks */}
<WithHeading/>
</div>
);
};

export default App;

D 部分:最终部分 PropEditor组件
PropEditor将在任何输入更改时调度一个 Action ,但请记住我们所有的 props被转换为用于渲染 UI 的对象数组,这些对象将在 PropForm 内部传递成分。

src/editor/components/PropEditor.js:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { PropForm } from './PropForm';
import { setPropValue } from '../PropActions';

export const PropEditor = () => {

// Alternative to connect’s mapStateToProps
const props = useSelector(state => {
return state;
});

// Alternative to connect’s mapDispatchToProps
// By default, the return value of `useDispatch` is the standard Dispatch type defined by the
// Redux core types, so no declarations are needed.
const dispatch = useDispatch();



const handleChange = (dataFromChild) => {
dispatch(setPropValue(dataFromChild));

};

// Convert objects into array of objects
const propsArray = Object.keys(props).map(key => {
return props[key];
});


return (
<div>
{/* Editor */}
<div style={styles.editor}>
<div style={styles.container}>
{ propsArray.length === 0
? <h1 style={styles.noProps}>No Props</h1>
: <PropForm props={propsArray} onFieldChange={handleChange} />
}
</div>
</div>
</div>
);
};

src/editor/components/PropForm.js:
import React from 'react';
import PropTypes from 'prop-types';
import TypeMap from './types';

const propTypes = {
props: PropTypes.arrayOf(PropTypes.object).isRequired,
onFieldChange: PropTypes.func.isRequired
};

// InvalidType component
const InvalidType = () => (<span>Invalid Type</span>);

export const PropForm = (properties) => {

/**
* @param name {string} - Name of the prop
* @param type {string} - InputType of the prop
* @return {Function} - Returns a function
*/
const makeChangeHandler = (name, type) => {
const { onFieldChange } = properties;
return (value = '') => {
// `change` will be an object and value will be from the onChange
const change = {name, type, value};
onFieldChange(change);
};
};
// Take props from the component properties
const { props } = properties;

return (
<form>
{
props.map(prop => {
const changeHandler = makeChangeHandler(prop.name, prop.type);
// Returns a component based on the `type`
// if the `type` is boolean then
// return Boolean() component
let InputType = TypeMap[prop.type] || InvalidType;
return (
<div style={{marginBottom: '16px'}} key={prop.name}>
<label htmlFor={prop.name}>{`${prop.name}`}</label>
<InputType prop={prop} onChange={changeHandler}/>
</div>
);
})
}
</form>
);
};

PropForm.propTypes = propTypes;

在所有这些解释之后,我的代码运行良好。

问题是重新渲染 Heading SET_PROP_VALUE 时组件未发生操作是在 PropEditor 内的输入更改时分派(dispatch)的成分。

Redux Store Debugging

正如您在 Redux DevTools 上看到的那样,商店发生了完美的变化。扩展但重新渲染组件 Heading没有发生。

我想是因为我的 HOC text()boolean()函数没有返回更新的值。

有没有办法解决这个问题?

请不要提这个我必须连接我的 WithHeading组件与 react-redux .我知道这一点,但有没有办法像 boolean('hasFruit', true) 这样的功能和 text('fruitName', 'Apple')更新商店状态时返回最新值?

代码沙盒: Sandbox

存储库: Repository

最佳答案

我在这里创建了 4 个演示,每个演示都是前一个演示的扩展版本:

1) 通过 mapStateToProps 连接痛点和更新组件

2) 通过使用 useSelector

 const boolVal = useSelector(state => state.hasFruit ? state.hasFruit.value : false );

3) 将动态名称设置为 useSelector
const booleanVal = useSelector(state => booleanSelector(state, "hasFruit"));

4) 创建了一个自定义钩子(Hook),以便您只需传递名称即可获得更新的值
const booleanVal = useGetValueFromStore("hasFruit");

The problem is re-rendering of the Heading component is not happening



原因:

是的,因为它没有连接到商店,它怎么知道 store 上发生了一些变化,您需要拨打 connect与商店建立联系并了解最新变化。

这是 blocks.js的更新代码:
// WithHeading Block
const WithHeading = props => {

useEffect(() => {
boolean("hasFruit", true); // <--- Setting initial value
text("fruitName", "Apple"); // <--- Setting initial value
}, []); // <----- get called only on mount

return <Heading hasFruit={props.boolVal} fruitName={props.textVal} />;

};

// to get updated state values inside the component as props
const mapStateToProps = state => {
return {
boolVal: state.hasFruit ? state.hasFruit.value : false,
textVal: state.fruitName ? state.fruitName.value : ""
};
};

// to make connection with store
export default connect(mapStateToProps)(WithHeading);

1) 工作演示 :

Edit #SO-redux-connect

另一种方法是您可以使用 useSelector :
// WithHeading Block
const WithHeading = props => {
// console.log(props);
const boolVal = useSelector(state =>
state.hasFruit ? state.hasFruit.value : false
);
const textVal = useSelector(state =>
state.fruitName ? state.fruitName.value : ""
);

useEffect(() => {
boolean("hasFruit", true);
text("fruitName", "Apple");
}, []);

return <Heading hasFruit={boolVal} fruitName={textVal} />;
};

export default WithHeading;

2) 工作演示:

Edit #SO-redux-connect2

您也可以将选择器放在单独的文件中,以便您可以随时使用它
const WithHeading = props => {
// you can pass the input names here, and get value of it
const booleanVal = useSelector(state => booleanSelector(state, "hasFruit"));
const textVal = useSelector(state => textValSelector(state, "fruitName"));

useEffect(() => {
boolean("hasFruit", true);
text("fruitName", "Apple");
}, []);

return <Heading hasFruit={booleanVal} fruitName={textVal} />;
};

3) 工作演示:

Edit #SO-redux-connect3

使用 useSelector 的自定义 Hook :
// a function that will return updated value of given name
const useGetValueFromStore = name => {
const value = useSelector(state => (state[name] ? state[name].value : ""));
return value;
};

// WithHeading Block
const WithHeading = props => {

//------- all you need is just to pass the name --------
const booleanVal = useGetValueFromStore("hasFruit");
const textVal = useGetValueFromStore("fruitName");

useEffect(() => {
boolean("hasFruit", true);
text("fruitName", "Apple");
}, []);

return <Heading hasFruit={booleanVal} fruitName={textVal} />;
};

export default WithHeading;

4) 工作演示:

Edit #SO-redux-connect4

关于reactjs - 当 store 状态改变时重新渲染组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62309220/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com